To detect physical interactions between game objects, the most common method is to use a Collider component—an invisible net that surrounds an object's shape and is in charge of detecting collisions with other objects. The act of detecting and retrieving information from these collisions is known as collision detection.
Not only can we detect when two colliders interact, but we can also pre-empt a collision and perform many other useful tasks by utilizing a technique called Ray Casting, which draws a Ray—put simply, an invisible (non-rendered) vector line between two points in 3D space—which can also be used to detect an intersection with a game object's collider. Ray casting can also be used to retrieve lots of other useful information such as the length of the ray (therefore—distance), and the point of impact of the end of the line.
In the given example, a ray facing the forward direction from our character is demonstrated. In addition to the direction, a ray can also be given a specific length, or allowed to cast until it finds an object.
Over the course of the article, we will work with the outpost model. Because this asset has been animated for us, the animation of the outpost's door opening and closing is ready to be triggered—once the model is placed into our scene. This can be done with either collision detection or ray casting, and we will explore what you will need to do to implement either approach.
Let's begin by looking at collision detection and when it may be appropriate to use ray casting instead of, or in complement to, collision detection.
When objects collide in any game engine, information about the collision event becomes available. By recording a variety of information upon the moment of impact, the game engine can respond in a realistic manner. For example, in a game involving physics, if an object falls to the ground from a height, then the engine needs to know which part of the object hit the ground first. With that information, it can correctly and realistically control the object's reaction to the impact.
Of course, Unity handles these kinds of collisions and stores the information on your behalf, and you only have to retrieve it in order to do something with it.
In the example of opening a door, we would need to detect collisions between the player character's collider and a collider on or near the door. It would make little sense to detect collisions elsewhere, as we would likely need to trigger the animation of the door when the player is near enough to walk through it, or to expect it to open for them.
As a result, we would check for collisions between the player character's collider and the door's collider. However, we would need to extend the depth of the door's collider so that the player character's collider did not need to be pressed up against the door in order to trigger a collision, as shown in the following illustration. However, the problem with extending the depth of the collider is that the game interaction with it becomes unrealistic.
In the example of our door, the extended collider protruding from the visual surface of the door would mean that we would bump into an invisible surface which would cause our character to stop in their tracks, and although we would use this collision to trigger the opening of the door through animation, the initial bump into the extended collider would seem unnatural to the player and thus detract from their immersion in the game. So while collision detection will work perfectly well between the player character collider and the door collider, there are drawbacks that call for us as creative game developers to look for a more intuitive approach, and this is where ray casting comes in.
While we can detect collisions between the player character's collider and a collider that fits the door object, a more appropriate method may be to check for when the player character is facing the door we are expecting to open and is within a certain distance of this door. This can be done by casting a ray forward from the player's forward direction and restricting its length. This means that when approaching the door, the player needn't walk right up to it—or bump into an extended collider—in order for it to be detected. It also ensures that the player cannot walk up to the door facing away from it and still open it—with ray casting they must be facing the door in order to use it, which makes sense.
In common usage, ray casting is done where collision detection is simply too imprecise to respond correctly. For example, reactions that need to occur with a frame-by-frame level of detail may occur too quickly for a collision to take place. In this instance, we need to preemptively detect whether a collision is likely to occur rather than the collision itself. Let's look at a practical example of this problem.
The frame miss
In the example of a gun in a 3D shooter game, ray casting is used to predict the impact of a gunshot when a gun is fired. Because of the speed of an actual bullet, simulating the flight path of a bullet heading toward a target is very difficult to visually represent in a way that would satisfy and make sense to the player. This is down to the frame-based nature of the way in which games are rendered.
If you consider that when a real gun is fired, it takes a tiny amount of time to reach its target—and as far as an observer is concerned it could be said to happen instantly—we can assume that even when rendering over 25 frames of our game per second, the bullet would need to have reached its target within only a few frames.
In the example above, a bullet is fired from a gun. In order to make the bullet realistic, it will have to move at a speed of 500 feet per second. If the frame rate is 25 frames per second, then the bullet moves at 20 feet per frame. The problem with this is a person is about 2 feet in diameter, which means that the bullet will very likely miss the enemies shown at 5 and 25 feet away that would be hit. This is where prediction comes into play.
Predictive collision detection
Instead of checking for a collision with an actual bullet object, we find out whether a fired bullet will hit its target. By casting a ray forward from the gun object (thus using its forward direction) on the same frame that the player presses the fire button, we can immediately check which objects intersect the ray.
We can do this because rays are drawn immediately. Think of them like a laser pointer—when you switch on the laser, we do not see the light moving forward because it travels at the speed of light—to us it simply appears.
Rays work in the same way, so that whenever the player in a ray-based shooting game presses fire, they draw a ray in the direction that they are aiming. With this ray, they can retrieve information on the collider that is hit. Moreover, by identifying the collider, the game object itself can be addressed and scripted to behave accordingly. Even detailed information, such as the point of impact, can be returned and used to affect the resultant reaction, for example, causing the enemy to recoil in a particular direction.
In our shooting game example, we would likely invoke scripting to kill or physically repel the enemy whose collider the ray hits, and as a result of the immediacy of rays, we can do this on the frame after the ray collides with, or intersects the enemy collider. This gives the effect of a real gunshot because the reaction is registered immediately.
It is also worth noting that shooting games often use the otherwise invisible rays to render brief visible lines to help with aim and give the player visual feedback, but do not confuse these lines with ray casts because the rays are simply used as a path for line rendering.
Adding the outpost
Before we begin to use both collision detection and ray casting to open the door of our outpost, we'll need to introduce it to the scene.
To begin, drag the outpost model from the Project panel to the Scene view and drop it anywhere—bear in mind you cannot position it when you drag-and-drop; this is done once you have dropped the model (that is, let go off the mouse).
Once the outpost is in the Scene, you'll notice its name has also appeared in the Hierarchy panel and that it has automatically become selected. Now you're ready to position and scale it!
Select the Transform tool and position your outpost in a free area of land by dragging the axis handles in the scene.
Be careful when using the axis handles for positioning. Dragging the white square where the handles converge will adjust all three axes at once—not something you'll want to use when in perspective mode, so ensure that you drag handles individually by keeping your cursor outside of the white square.
If you'd like to constrain an object to the surface of the terrain in perspective mode, then you can hold the Command key (Mac) or Ctrl key (PC) and drag within the white square.
I have positioned my outpost at (500, 30.8, 505) but you may need to reposition your object manually. Remember that once you have positioned using the axis handles in the Scene window, you can enter specific values in the Position values of the Transform component in the Inspector.
As we are working with two applications—the modeling application our outpost asset was created in and Unity itself—often the scale of the imported asset will not match the meter units in Unity and will need scaling up or down.
While we can set up Scale values in the Inspector, it is best wherever possible, to set up your scale by using the Scale Factor setting on the original asset. Select the outpost model inside the Outpost folder in the Project panel. Now in the Inspector, you will see a component called FBXImporter. This component has import settings available that will affect any instances of the model you place into the Scene. Here you should set the Scale Factor value from 1 to 2. This makes the outpost model large enough for our Character Controller to fit through the doorframe and making it a realistically sized room once we get inside.
Colliders and tagging the door
In order to open the door, we need to identify it as an individual object when it is collided with by the player—this can be done because the object has a collider component and via this we can check the object for a specific tag. Expand theoutpost parent object by clicking on the dark gray arrow to the left of its name in the Hierarchy panel.
You should now see the list of all child objects beneath it. Select the object nameddoor and then with your mouse cursor over the Scene window, press F on the keyboard to focus your view on it.
You should now see the door in the Scene window, and as a result of selecting the object, you should also see its components listed in the Inspector panel. You should notice that one of the components is a Mesh Collider. This is a detailed collider assigned to all meshes found on the various children of a model when you select Generate Colliders.
The mesh collider is assigned to each child element, as Unity does not know how much detail will be present in any given model you could choose to import. As a result, it defaults to assigning mesh colliders for each part, as they will naturally fit to the shape of the mesh they encounter. Because our door is simply a cube shape, we should replace this mesh collider with a simpler and more efficient box collider.
From the top menu, go to Component | Physics | Box Collider.
You will then receive two prompts. Firstly, you will be told that adding a new component will cause this object to lose its connection with the parent object in the Project panel. This dialog window, titled Losing Prefab, simply means that your copy in the Scene will no longer match the original asset, and as a result, any changes made to the asset in the Project panel in Unity will not be reflected in thecopy in the Scene. Simply click on the Add button to confirm that this is what you want to do.
This will happen whenever you begin to customize your imported models in Unity, and it is nothing to worry about. This is because, generally, you will need to add components to a model, which is why Unity gives you the opportunity to create your own prefabs.
Secondly, as the object already has a collider assigned to it, you will be prompted whether you wish to Add, Replace, or Cancel this collider to your object. Generally, you'll use a single collider per object, as this works better for the physics engine in Unity. This is why Unity asks if you'd like to Add or Replace rather than assuming the addition of colliders.
As we have no further need for the mesh collider, choose Replace.
You will now see a green outline around the door representing the Box Collider component you have added.
A box collider is an example of a Primitive Collider, so called as it is one of several scalable primitive shape colliders in Unity—including Box, Sphere, Capsule, and Wheel—that have predetermined collider shapes, and in Unity, all primitive colliders are shown with this green outline. You may have noticed this when viewing the character controller collider, which is technically a capsule collider shape and as such also displays in green.
Finally, we need to tag the door object, as we will need to address this object in our scripting later. With the door child object still selected, click on the Tag drop-down at the top of the Inspector panel, and choose Add Tag. In the Tag Manager that replaces your current Inspector view, add the tag outpostDoor, as shown in the following screenshot:
Because adding tags is a two step process, you will need to reselect the door child object in the Hierarchy panel, and choose your newly added outpostDoor tag from the Tag drop-down menu to finish adding the tag.
Disabling automatic animation
By default, Unity assumes that all animated objects introduced to the scene will need to be played automatically. Although this is why we create an idle animation—in which our asset is doing nothing; allowing Unity to play automatically will often cause animated objects to appear a frame into one of their intended animations. To correct this issue, we simply deselect the Play Automatically checkbox in the Inspector for the parent object of our model. Ordinarily, we would not need to world. For example, a billowing flag or rotating lighthouse lamp, but we need our outpost not to animate until the player reaches the door, so we avoid automatically playing any animation.
To do this, reselect the parent object called outpost in the Hierarchy panel, and in the Animation component in the Inspector panel, deselect Play Automatically.
The Inspector panel view of the outpost object should now look like the following screenshot:
Note that I have expanded the Animations parameter here in the Animation component to view the animation states currently applied to this object.
In this article, we learned how to add an outpost model to our project. We also learned how to position, scale, assign colliders to objects as well as tag objects.
The outpost object is now ready to be interacted with by our player character, so in the next part we will begin scripting to detect collisions with our newly tagged door using either collision detection or ray casting as outlined above.