Mauro Fuentes I am a 3D artist enthusiast, C# programmer, Unity game developer, and Kung fu Shaolin instructor.

Understanding 2D and 3D Raycasting in Unity

12 min read 3569

Understanding 2D and 3D Raycasting In Unity

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

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:

Unity Example Screenshot

Daunting, huh?

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.



Unity Engine Classes

Now, let’s delve into the Physics class itself.

What if we grouped over 100 methods into just three simple categories? They will be so much easier to remember. We just need to give them a nice name.

Like this:

Unity Physics Classes

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 its own system, Unity adapted its own naming conventions according to its 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.


More great articles from LogRocket:


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 in 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.

Physics scene

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.

Physics Scene Example 1

Physics Scene Example 2

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 witch’s stew brew where all physics is 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.

Physics Query Collider Rigidbody

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:

Physics Query Capsule Caster

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.

Recap

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.

Checkers

  • 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

Casters

  • 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

Overlappers

  • They don’t sweep or move at all
  • They only retrieve Arrays (of Colliders)
  • They care if two things are in 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 in 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 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.

Physics Query Visual Representation

Inside, you will find a visual representation of the most important Physics query 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 are 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!

NonAlloc

In this section, we will discuss the relationship between physics queries, memory allocation, buffering, and NonAlloc queries.

Alloc

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 for 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.

Buffer

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:

  1. Buffer everything instead of creating things at runtime.
  2. Look for 0 irresponsible allocations whenever you can.

NonAlloc

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.

For instance:

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

Name Ray arg arg arg arg arg arg arg OUT Takes IN Returns
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
Checkers CheckBox center
halfExtents
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
Overlappers OverlapBox center
halfExtents
orientation layerMask queryTriggerInteraction Collider[] retrieving all hits (uncontrolled)
Overlappers OverlapBoxNonAlloc center
halfExtents
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.

Physics Class Flowchart

As a final thought: remember what we said earlier — don’t lose your head on micro-optimizations, 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:

Caster Screen Example

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:

Unity Example Screenshot

We have turned it into this:

Capsule Caster Clean Example

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.

Performance

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?

Or

Somewhat slow code that creates a bit of garbage, but it’s short, easy to read, and maintainable by juniors?

Tests

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:

Test:

  • 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 multiplied by 60 seconds to estimate the garbage in 1 minute if not collected

The Ugly

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!

The Bad

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.

The Good

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 the 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.

Box Caster Ignore Layer

The code looks like this:

Box Caster Ignore Layer Code

PrivateVoidPerformCast

If you want to add more layers — simply choose more in the inspector.

Conclusion

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!

: 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.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Mauro Fuentes I am a 3D artist enthusiast, C# programmer, Unity game developer, and Kung fu Shaolin instructor.

Leave a Reply