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

Sorta physics question, detecting collisions without rigidbodies, odd shaped colliders.

Discussion in 'Scripting' started by UnbridledGames, Jul 4, 2021.

  1. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    So I'm working on something and I'm finding the need to detect collisions between objects when they are created, but these objects usually do not have rigidbodies and don't need to be triggers.

    An example would be a better way to explain it.

    There are craftable build pieces in the world, floors, walls, etc. All have colliders, but no rigidbodies on them. No need, they don't ever move. These build pieces have support values, build too high or off the center of mass too much, it could break apart. This is all calculated when the objects are created. Add a new build piece, and necessary supports are calculated.

    I found an edge case that I hadn't planned for and am trying to find a 'universal' way to handle it, so I don't have to handle each possible edge case with custom code.

    Trees can support structures as well. Build against a tree, it is for all intents and purposes the "ground" and offers the best support. Cool.

    You can also plant trees. The problem is that when the tree grows full size, no building pieces it may touch are aware of the new tree, and their support values never update. Same with cutting down a tree. Chop it down, the treehouse attached to it floats there.

    I'm trying to find a way to detect collision between colliders with no attached ridigbodies. So when a tree 'appears' anything it may touch is aware it appears. Yes I could add code to the tree growing, but there are other objects that could theoretically appear or disappear near built objects that also do not have rigidbodies, and custom writing scripts to do a Physics.Overlap[Shape] and find and use the position/size for each of the possible objects is a pain. Is there any way to do an overlap check with a collider that already exists? Grow the tree, check if it's existing collider is overlapping anything?

    I really don't want to add rigidbodies to things just to be able to do one-time collision detection, however there are objects that COULD appear/move into the scene, without rigidbodies, that I would need to do 1 time collision tests for.
     
  2. TheFunnySide

    TheFunnySide

    Joined:
    Nov 17, 2018
    Posts:
    192
  3. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    Yeah thats actually what I'm doing now, problem is for the trees anyway, the trunk is a mesh collider, which has branches coming off of it at angles... so even using a box or capsule based on the extents, I'm getting a HUGE area around the tree when I only really need anything touching the trunk. Its super frustrating that I cannot add a rigidbody and have that trigger OnCollisions then remove it... the objects actually need to MOVE for the Collision events to happen it seems... and neither of these objects actually move.
     
  4. olejuer

    olejuer

    Joined:
    Dec 1, 2014
    Posts:
    210
    There is OnTriggerStay(Collider) and OnCollisionStay(Collision)
    https://docs.unity3d.com/ScriptReference/Collider.OnTriggerStay.html
    https://docs.unity3d.com/ScriptReference/Collider.OnCollisionStay.html

    Those don't require the objects to move, because they don't require the collision to be 'new'.

    Hints
    I would suggest you create dedicated primitive colliders (box, sphere, capsule) in a separate layer to detect your collisions. It's manual effort to create those, unfortunately.
    When you absolutely need to have the collisions registered at the time of creation (meaning before the first Physics update), you will have to manually check for collisions using Physics.OverlapBox() etc.
    When you need the precise shape of your mesh collider, I think you can try
    Physics.ComputePenetration() https://docs.unity3d.com/ScriptReference/Physics.ComputePenetration.html

    A note of caution
    Be careful with manual Physics checks and transforms. Getting the parameters right when your collider is in a hierarchy of rotated and scaled objects can be quite tricky. You can spend a lot of time on getting this stuff right.

    A note on time and updates:
    Regular collision checks run in Physics Update (the one that runs FixedUpdate()) and therefore not necessarily every frame. This depends on the frame rate (let's call it the render frame rate, so the one that runs Update()) and your setting for Fixed Timestep (0.02s by default). When your game time runs for a period of time longer than the Fixed Timestep, you are guaranteed that Physics Update has started. However, things can get messed up when performance gets very bad and the maximum allowed timestep is exceeded. Also note that game time can be slowed down relative to actual wall-clock time in case of bad performance.
    Here is a great article on Unitys time management: https://johnaustin.io/articles/2019/fix-your-unity-timestep
     
    DanDevSC likes this.
  5. olejuer

    olejuer

    Joined:
    Dec 1, 2014
    Posts:
    210
    I am periodically bothered by this, so I wrote a utility. You can attach it to a GameObject with a Collider Component and it will draw a red gizmo when there is a collision or a green one otherwise. It uses OverlapBoxNonAlloc to get a candidate list of colliders that might overlap and then checks with ComputePenetration if there actually is an overlap. Note that this can become performance intense.

    Code (CSharp):
    1. [RequireComponent(typeof(Collider))]
    2. public class CollisionChecker : MonoBehaviour
    3. {
    4.     [SerializeField] private LayerMask _layerMask;
    5.     private Collider _collider;
    6.     private readonly Collider[] _colliderCache = new Collider[32];
    7.  
    8.     private void OnDrawGizmos()
    9.     {
    10.         _collider = GetComponent<Collider>();
    11.         var isOverlapping = false;
    12.      
    13.         // iterate over all colliders in bounds.
    14.         int hitCount = Physics.OverlapBoxNonAlloc(_collider.bounds.center, _collider.bounds.extents, _colliderCache,
    15.             _collider.transform.rotation, _layerMask);
    16.         for (var i = 0; i < hitCount; i++)
    17.         {
    18.             if (_colliderCache[i] == _collider) continue; // don't detect self-collision
    19.             isOverlapping |= Physics.ComputePenetration(
    20.                 _collider,
    21.                 _collider.transform.position,
    22.                 _collider.transform.rotation,
    23.                 _colliderCache[i],
    24.                 _colliderCache[i].transform.position,
    25.                 _colliderCache[i].transform.rotation,
    26.                 out Vector3 _, out float _);
    27.         }
    28.  
    29.         Gizmos.color = isOverlapping ? Color.red : Color.green;
    30.         Gizmos.DrawSphere(transform.position, 0.1f);
    31.     }
    32. }