How does raycast work under the hood? With 300 sphere colliders

Does a raycast split all the colliders into octree like groups ie

Or does it just go:
for each sphere collider i=0 i <300 i++
detectcollision()

Is it worthwhile trying to optimise out icons to test against ?

I have about 300 icons on a muli-terrain (about 16 terrains) system.

The icons can stick above the terrain. We have tried optimising by only raycasting against the icons in the terrain that was hit but there are times when the raycast goes through an icon and hits the terrain on the other side, and we don’t get a collision detection.

I could make boxes that incorporate all the icons, and use that instead, but was wondering if doing that was worth it? ie raycast might already do something better.

ie for some context - I need to click on the triangles, at this low angle.

            LayerMask maskIcon = LayerMask.GetMask("Icon");
            if (Physics.Raycast(worldRay, out hit, Mathf.Infinity, maskIcon))
            {
                GameObject objectHit = hit.transform.gameObject;
                Debug.DrawRay(worldRay.origin, worldRay.direction * hit.distance, Color.yellow);
                if (!string.IsNullOrEmpty(objectHit.name))
                {
                    Debug.Log("Did Hit " + objectHit.name);
                }
                return true;
            }
            else
            {
                Debug.DrawRay(worldRay.origin, worldRay.direction * 1000, Color.white);
            }

This all just sounds like speculative optimization to me.

DO NOT OPTIMIZE “JUST BECAUSE…” If you don’t have a problem, DO NOT OPTIMIZE!

If you DO have a problem, there is only ONE way to find out. Always start by using the profiler:

Window → Analysis → Profiler

Failure to use the profiler first means you’re just guessing, making a mess of your code for no good reason.

Not only that but performance on platform A will likely be completely different than platform B. Test on the platform(s) that you care about, and test to the extent that it is worth your effort, and no more.

Remember that optimized code is ALWAYS harder to work with and more brittle, making subsequent feature development difficult or impossible, or incurring massive technical debt on future development.

Notes on optimizing UnityEngine.UI setups:

At a minimum you want to clearly understand what performance issues you are having:

  • running too slowly?
  • loading too slowly?
  • using too much runtime memory?
  • final bundle too large?
  • too much network traffic?
  • something else?

If you are unable to engage the profiler, then your next solution is gross guessing changes, such as “reimport all textures as 32x32 tiny textures” or “replace some complex 3D objects with cubes/capsules” to try and figure out what is bogging you down.

Each experiment you do may give you intel about what is causing the performance issue that you identified. More importantly let you eliminate candidates for optimization. For instance if you swap out your biggest textures with 32x32 stamps and you STILL have a problem, you may be able to eliminate textures as an issue and move onto something else.

This sort of speculative optimization assumes you’re properly using source control so it takes one click to revert to the way your project was before if there is no improvement, while carefully making notes about what you have tried and more importantly what results it has had.

1 Like

No. A raycast queries the spatial database that is already set-up when the simulation runs. It doesn’t go to every collider, create some kind of spatial database then query it; that would give you horrible performance and have to be done each query.

Queries are already optimized. You just have to use them.

4 Likes

A different programmer has written the collision code and it works about 90% of the time.The problem is that they’ve written a wall of maths, and it’d take me 2-3 weeks to figure out the 1 or 2 lines that are broken. So I thought I’d just dump all the specialist collision code and use sphere colliders/raycast instead. I’ll try both and see what one is faster. I haven’t used unity profiler before - I was going to count the milliseconds each process takes to run.

I’m trying to make the code simpler. It’s a process I call simp-ing. All my friends call me a simp because I’m good at making code simpler.

Raycast is faster than any shape-cast but for a sphere cast it’s only going to be slightly faster but you need to be doing a lot of them to make any difference. Typically the code surrounding them processing the results is the dominating factor, not the queries themselves.

3D physics has a batch raycast: Unity - Scripting API: RaycastCommand

1 Like

Hi -
Thanks for the ideas and help.

I end up doing 2D collison detection - because the icon are actually 2D, but placed in 3d world and stay realatively the same size. Runs really fast at 6~7 micro seconds. Only recently discovered Camera.WorldToScreenPoint(). Anyway, 500-1000 lines of code is now about 10 lines of code, it’s accurate and crazy fast. A great result on every metric!

 // 2d Collison - We get the screen co-ordinates of the icon (using Camera.WorldToScreenPoint() ) and mouse and do a radius check to see if the mouse is inside the radius in pixels of the point.
        public static bool MouseOver(Vector3d relativePosiiton /* <-- not needed */, out float Weight)
        {
            // Not needed if you copy past this code. This converts our double point precision point to floating point
            Vector3 pos = relativePosiiton.ToRelativeWorldSpace();
            ////////
            const float RADIUS_SQUARE_IN_PIXELS = 400.0f; // ie Mathf.Pow(20f, 2));
            Vector3 screenPos = Viewport.Main.Camera.WorldToScreenPoint(pos);
            float SqrMag = Vector3.SqrMagnitude(screenPos - Input.mousePosition);
            if (SqrMag < RADIUS_SQUARE_IN_PIXELS)
            {
                // How close to the center, only needed if 2 or more icons detect collision ( ie which one is closer?
                Weight = SqrMag / RADIUS_SQUARE_IN_PIXELS;
                return true;
            }
            Weight = 1.0f;
            return false;
        }