Raycasting is often the subject of heated debate in tutorials, forums, and blogs. Not only from enthusiasts, but also advanced developers who tend to have difficulties understanding the nuances of the Physics class.
It’s fine if you’re not completely familiar with Unity’s official documentation. At the expense of exhaustive and detailed explanations, they take a general and simplistic approach in order to cover more terrain.
That’s why I’m so glad you’ve found this article as we explore Raycasting in detail!
We wanted to go deep on this one, and that’s why this article has a very theoretical approach. The main idea is to help everyone really understand what’s going on under the hood of Raycasting in Unity.
We also hope you have fun and find this article an all-in-one resource for Raycasting in Unity.
- Making sense of Physics in Unity
- Raycasting might not be what you think it is
- Physics scene
- What a Physics query looks like
- Explaining Checkers, Casters, and Overlappers
- Breaking Newton’s third law
- Explaining the repo
- Fact or trick
- Layers: ignoring one or multiple layers
Making sense of Physics in Unity
Have you ever felt unsure of which method to choose? Or hesitant about what makes each method different?
That’s normal. Consider that Unity gives you 22 methods out of the box, and if you take into account overloads, you get over 100 different methods to use in your scene — that’s a lot!
Oh, and that’s just for 3D. No wonder people panic!
Let’s take a peek inside Unity:
Don’t worry. After all, Physics is just another class inside Unity engine.
Let’s try to frame the Physics class by adding some context. What we want here is to know where the class sits inside the engine.
Now, let’s delve into the Physics class itself.
What if we just grouped the one hundred and something methods in just three simple categories? They will be so much easier to remember. We just need to give them a nice name.
More great articles from LogRocket:
- Don't miss a moment with The Replay, a curated newsletter from LogRocket
- Use React's useEffect to optimize your application's performance
- Switch between multiple versions of Node
- Learn how to animate your React app with AnimXYZ
- Explore Tauri, a new framework for building binaries
- Compare NestJS vs. Express.js
- Discover popular ORMs used in the TypeScript landscape
Just look at that! Neat!
We’ll see the rationale behind this grouping later, but, for now, let’s continue with some key concepts.
Raycasting might not be what you think it is
We’ve all used the expression “ray casting” at some point. It sounds logical, but what if you knew that’s not entirely accurate or useful in Unity?
Of all those over 100 methods mentioned above, the only group that uses Rays is Casters.
You see, to make sense of their own system, Unity adapted its own naming conventions according to their own needs. It seems to be quite evident that they adapted classes from Nvidia’s PhysX framework, but as this convention is not really explicit, people can get confused, and that’s why we analyzed the Physics class first.
A term that makes so much more sense in Unity’s context, and the one we will use from now on, is Physics queries.
If you think about it, that’s what it is: you are examining the physical world of your game, not just casting rays.
If left alone, Unity will work on the background, making lots of calculations. That’s where Physics queries come into the scene. They are the tools you have to assess what’s going on.
Speaking of which, we can’t talk about physics without mentioning Newton. Keep reading to find out why old Isaac could’ve been wrong.
Of course, you already know that a Scene in Unity is an asset, right?
What you might not know is that Unity stores lots of things inside a Scene other than just gameObjects and Transforms.
One important component of a Scene is the PhysicsScene.
Check this out! Go to Window>Analysis>Physics Debugger and marvel! You now have access to a David Lynch-style alternative reality — think of it as the Unity’s Upside Down.
The PhysicsScene has its own classes and methods, and they are all related to Physics. This is your ultimate tool when it comes to debugging collisions and layers.
Take your time to explore all the settings this new window has; all you need about Physics is in here.
Although we don’t really know the internal workings, we know that the PhysicsScene works in conjunction with the PhysicsModule.
Both together, they are the witches stew brew where all physics are mixed down into one thing.
What a Physics query looks like
In this part, we will touch very briefly on the most basic concepts of Physics queries.
GameObjects need to comply with certain aspects to be considered by the Physics class. That’s why in the image below, we’ve drawn the Rigidbody inside the object, and the Collider as a sort of boundary.
As a general rule, you can think of Colliders as public communicators with the outside world, while Rigidbodies are more like the inner private properties an object has.
They both work in conjunction, but while Colliders have access to their Rigidbodies, Rigidbodies know nothing about Colliders.
All in all, both Colliders and Rigidbodies are used to find out what’s going on and what’s going to happen next in the physical world of your game.
But, what if you don’t want to wait to know what is going to happen? What if you need to know in advance, so you can act upon it? What if you don’t want to be there anyway?
The name of the technique that lets you determine that is, you guessed it — a Physics query.
In the next image, you will see a simple Physics query:
What you see there is a Source and a Target object. As we said, we want to know things in advance. Having access to it gives us tons of opportunities to code interesting reactions.
We can say that a Hit takes place when two Colliders are at the same time and space; they are overlapping. In other words, they are handshaking. That’s where RaycastHit comes from.
When two Rigidbodies are at the same time and space, we think of Penetration and Depenetration forces or collisions; but that’s for another tutorial.
Just as a brief recap of what you’ve seen so far. We saw what the Physics class is and where it sits in the engine. We’ve divided all classes into mnemonic groups. We’ve seen “the other side” of physics. We established some appropriate words to understand what’s going on at a technical level. And we’ve also explained what a physics query looks like.
Let’s go for more.
Explaining Checkers, Casters, and Overlappers
We will explain how and why we divided those three groups.
First and foremost. Let’s agree upon this: all Physics queries…
- probe space
- use GeometryCasters (lines, rays, spheres, cubes or capsules)
- can ignore layers
- can ignore Triggers
- can take part in Unity’s callbacks (events)
Now that you know what all queries have in common, let’s look at some differences.
- The simplest type of all
- They don’t sweep; they are stationary shapes.
- They don’t retrieve information other than true or false
They ask Unity: Did you hit something? Unity answers: Yes/No
- They move/sweep. The query travels through space
- They retrieve RaycastHit: a struct full of valuable information
- They retrieve bool
- They give info about one hit or multiple hits in the form of Arrays
- It’s easy to understand them if you think of future collisions (early detections)
- Ask Unity: I will throw this [shape] in a straight line. If it hits anything, give me all the RaycastHit data about it
- They don’t sweep or move at all
- They only retrieve Arrays (of Colliders)
- It’s easier to understand them if you think they see from inside out what’s going on
- They care if two things are at the same place at the same time.
- They ask Unity: I will put this [shape] in front of me. If something appears inside, give me all data about the Collider of all objects inside it.
Breaking Newton’s third law
Remember Newton’s third law? The one about action and reaction? Little did he know about video games.
One of the most curious things video games have is that despite our sincerest efforts to make them realistic, they will stay video games.
In video games, reactions do not necessarily follow an action. Video games give us this huge opportunity to hijack Newton’s third rule.
In video games, Newton’s third goes like this:
Action>possible analysis>possible reaction.
Using the word query makes so much more sense now. Those queries are in nature an early detection of the world. We can catch and analyze what’s happening. We can then act upon before something else happens.
To think, isn’t it the same that happens in literature?
Explaining the repo
The writer created a public repo with a Unity project for everyone to learn. If you still have problems wrapping your head around Raycasting in Unity, I hope this will help you understand.
Inside, you will find a visual representation of the most important Physics queries methods Unity has. They clearly depict what’s going on in each method. We will continue to add and polish that repo for you all.
You don’t even need to press play to see the queries in action; you can see it in editor time. And, what’s best, there’s no fancy tricks in the code. You can just grab the code and paste it in your games to try them out.
Please, out of courtesy, do reference us or the writer — not even Unity dared do that much!
In this section we will discuss the relationship between physics queries, memory allocation, buffering, and NonAlloc queries.
Every time you create something in code, you allocate memory. Because allocating means “to assign”. So, allocations are good… until they aren’t.
So, when are allocations a bad thing?
In general, allocations are bad when you:
- are not being responsible with the allocation
- don’t know what you are allocating
- lose control over the process
- forget about them
- aren’t going to use it anyway (look for YAGNI)
So, what’s the relationship with Physics queries?
That’s easy, each Physics query version is tailor-made to solve one or two of the above mentioned problems at a time. In time, each Physics query has its advantages and disadvantages.
Now, let’s move on to buffers.
Based on Unity’s documentation and programming best practices from the best of the best, let’s state some maxims here:
- Buffer everything instead of creating things at runtime.
- Look for 0 irresponsible allocations whenever you can.
In NonAlloc methods, Unity is making sure that you responsibly allocate memory (pre-allocate) before using it. Because arrays are a reference type, you can pass it as a parameter. Unity doesn’t care about the content of that array, it only cares about the size.
Take a look at this:
public static int OverlapSphereNonAlloc(Vector3 position, float radius, Collider results)
See that Unity is asking for an array of Colliders?
Think of it as a Rubik’s Cube. You give it to Unity, and internally it will shuffle the data before giving it back to you. The method won’t generate garbage this way, because Unity won’t have to create a new array.
That’s why it’s called NonAlloc, because they don’t allocate memory, you do!
Conversely, other versions create a new array every single time you call it.
public static Collider OverlapSphere(Vector3 position, float radius)
That means that if you call it again, it will forget the old array and hope_ _for the garbage collector to dispose of it.
Fact or trick
If you ever read Unity’s blogs or forums, you will realize that people lose their heads over this topic, we will try to do our best to explain what is going on!
In this section, we will state some general facts to help you reach your own conclusions and be tricked no more. We will give you tools to help you organize and choose the method you need.
Fact 1: You can’t call Unity’s Physics methods directly
|CASTER||CapsuleCast||point1||point2||radius||direction||maxDistance||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Caster||CapsuleCast||point1||point2||radius||direction||maxDistance||layerMask||queryTriggerInteraction||out RaycastHit||bool||retrieving 1 hit|
|Caster||CapsuleCastAll||point1||point2||radius||direction||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||retrieving all hits (uncontrolled)|
|Caster||CapsuleCastNonAlloc||point1||point2||radius||direction||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||int||retrieving controlled hits|
|Caster||SphereCast||origin||radius||direction||maxDistance||layerMask||queryTriggerInteraction||out RaycastHit||bool||retrieving 1 hit|
|Caster||SphereCast||ray||radius||maxDistance||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Caster||SphereCast||ray||radius||maxDistance||layerMask||queryTriggerInteraction||out RaycastHit||bool||retrieving 1 hit|
|Caster||SphereCastAll||origin||radius||direction||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||retrieving all hits (uncontrolled)|
|Caster||SphereCastAll||ray||radius||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||retrieving all hits (uncontrolled)|
|Caster||SphereCastNonAlloc||ray||radius||direction||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||int||retrieving controlled hits|
|Caster||SphereCastNonAlloc||origin||radius||direction||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||int||retrieving controlled hits|
|Caster||BoxCast||center||halfExtents||direction||orientation||maxDistance||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Caster||BoxCast||center||halfExtents||direction||orientation||maxDistance||layerMask||queryTriggerInteraction||out RaycastHit||bool||retrieving 1 hit|
|Caster||BoxCastAll||center||halfExtents||direction||orientation||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||retrieving all hits (uncontrolled)|
|Caster||BoxCastNonAlloc||center||halfExtents||direction||orientation||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||int||retrieving controlled hits|
|Caster||RayCast||origin||direction||maxDistance||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Caster||RayCast||origin||direction||maxDistance||layerMask||queryTriggerInteraction||out RaycastHit||bool||retrieving 1 hit|
|Caster||RayCast||ray||maxDistance||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Caster||RayCast||ray||maxDistance||layerMask||queryTriggerInteraction||out RaycastHit||bool||retrieving 1 hit|
|Caster||RayCastAll||origin||direction||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||retrieving all hits (uncontrolled)|
|Caster||RayCastAll||ray||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||retrieving all hits (uncontrolled)|
|Caster||RayCastNonAlloc||origin||direction||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||int||retrieving controlled hits|
|Caster||RayCastNonAlloc||ray||maxDistance||layerMask||queryTriggerInteraction||RaycastHit||int||retrieving controlled hits|
|Caster||LineCast||start||end||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Caster||LineCast||start||end||layerMask||queryTriggerInteraction||out RaycastHit||bool||retrieving 1 hit|
|orientation||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Checkers||CheckSphere||position||radius||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|Checkers||CheckCapsule||start||end||radius||layerMask||queryTriggerInteraction||bool||no retrieving hit|
|orientation||layerMask||queryTriggerInteraction||Collider||retrieving all hits (uncontrolled)|
|orientation||mask||queryTriggerInteraction||Collider||int||retrieving controlled hits|
|Overlappers||OverlapSphere||position||radius||layerMask||queryTriggerInteraction||Collider||retrieving all hits (uncontrolled)|
|Overlappers||OverlapSphereNonAlloc||position||radius||layerMask||queryTriggerInteraction||int||retrieving controlled hits|
|Overlappers||OverlapCapsule||point0||point1||radius||layerMask||queryTriggerInteraction||Collider||retrieving all hits (uncontrolled)|
|Overlappers||OverlapCapsuleNonAlloc||point0||point1||radius||layerMask||queryTriggerInteraction||int||retrieving controlled hits|
Most of Unity’s methods are private. What you are really calling are in fact wrapper methods (overloads, if you will). Even if you could, don’t forget Unity’s core is C++.
You call method wrappers; then they call other wrappers; wrappers call root methods; root methods call other classes; those classes have more internal wrappers and so on, until you hit the Unity C#/C++ wall. That’s just the way it is. You can’t control it, not change it.
To give you an idea, suppose you call the simplest version of CapsuleCast():
Physics.CapsuleCast(point1, point2, radius, direction, maxDistance);
Physics.CapsuleCast() (a method wrapper) calls the “root” CapsuleCast()
That root calls another CapsuleCast() inside the PhysicsScene.
From there, a call to the InternalCapsultCast() is made. From there, a query is created. From there, there’s a call to some class that asks for another version called Injected versions and there you hit the wall of the C#/C++ layer.
Fact 2: Call methods based only on what you need
So, you are still hesitant of what method to use. You ask yourself which one is faster or which one is better. No problem, we’ve prepared this approach to help you out. Notice, this image is for illustration purposes only.
As a final thought: remember what we said in 1. Don’t lose your head on microoptimizations. Just be responsible for your allocations.
Fact 3: Physics queries are 100 faces of the same coin
We went even further; we’ve created an all-in-one Google Sheet where you can compare and contrast all methods. You will soon realize that they have more in common than not. That’s because they have been really well planned for Unity standards.
You can search for each method. In this example, let’s see how to filter only for methods that use ray as parameters:
Fact 4: The myth of boilerplate
If we go back to the first fact; the whole C# layer is boilerplate if you think about it. Now, wrappers are not boilerplate, since they are tools that help you filter to choose a method that suits your needs.
But anyway, we went ahead and also prepared another type of file to help out — it’s the
helper.cs in the repo.
Remember this ugly thing:
We have turned it into this:
Notice that helper.cs is a stripped human-readable code version and must not be used for coding as is. This file just portrays Physics classes in a tidy way.
One final note. We don’t want to sound pessimistic here, but what is it about performance anyway?
What’s more performant?
Superfast code you don’t understand, you can’t maintain, you don’t control but doesn’t create garbage?
Somewhat slow code that creates a bit of garbage, but it’s short, easy to read, and maintainable by juniors?
As a disclaimer, these tests were run using the normal Unity profiler. We weren’t concerned about the factual raw values, but the proportional contrast in (garbage) performance. You can test it too, we’ve provided a scene where you can create yours.
The three test cases were run against 1400 colliders. Each case is two minutes’ long. The method used is BoxOverlap.
The structure of tests is this:
- Time: Time the task took to complete that frame: X milliseconds (ms)
- Garbage per frame: X amount in KiloBytes (KB) of garbage generated per frame (/frame)
- Garbage per second: X amount of garbage multiplied by 60 frames, so we get the amount in 1 second
- Garbage per minute: That result is multiply by 60 seconds to estimate the garbage in 1 minute if not collected
In this case, we use the BoxOverlap method, but with the worst idea possible. To cache the array inside the Update function. Believe it or not, there was a time when Unity developers recommended this practice.
- Time: 0.83 ms
- Garbage per frame: 22.5 KB G/F
- Garbage per second: 22.5 KB G/F x 60 F = 1350 KB G/s [1.35 MB G/s]
- Garbage per minute: 1350 KB s x 60 s = 80001 KB G/m [80 MB G/m]
Did you see that? Imagine if you are running with 1 GB of memory. Your game not only takes 80 MB of trash per minute, but also it will certainly freeze or stutter every time it has to dump so much garbage and only to allocate 1 simple list. That’s ugly!
This case is the same as before, but it caches the array beforehand and outside the Update method.
- Time: 0.77 ms
- Garbage per frame: 11.8 KB G/F
- Garbage per second: 11.8 KB G/F x 60 F = 708 KB G/s
- Garbage per minute: 708 KB x 60 s = 42 MB G/min
That’s half the ugly as before.
There’s not much to say.
- Time: 0.1ms
- Garbage per frame: 0 B G/F
- Garbage per second: 0 x 60 x 60 = 0 B G/s
- Garbage per minute: Heaven on earth!
Layers: ignoring one or multiple layers
We said that every single Physics query can ignore layers.
The name of the struct is LayerMask and can fool you because it is singular. But this special struct is directly wired to Unity’s guts, so it knows all your layers in your project. That’s perfect because you only need one to access all.
If you look up in the 3D scene inside the repo, you will find a Box Cast Ignore Layer. Those gameObjects have a very special (purple) type of cube. If you click it, you’ll find that it belongs to a custom layer called “SpecialCube”.
Now, if you select the Reporter, you’ll find that it has the Script “BoxCasterIgnoreLayer”. Ignoring the layer, where the “SpecialCube” is, it’s as simple as choosing that layer and pass it to the Physics query.
The code looks like this:
If you want to add more layers — simply choose more in the inspector.
Now that you’ve finished this article, we hope your understanding of Unity’s Physics is far more comprehensive now.
Remember that now you have a Unity project with all queries, a Helper class with clean, readable text, and a Google sheet to filter classes — oh, and a drawio file to help you decide which method to use.
Feel free to contact us if you feel we should add something.
We know this article is more theoretical than a hands-on approach. But, now you have the basis to understand all about Physics class both 3D and 2D, because this article paves the ground for lots of future content.
Don’t hesitate to comment or contact us. Enjoy!
LogRocket: Full visibility into your web and mobile apps
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.