Turning the player with raycasts

At this point, you can fire a weapon and push the alien around. Unfortunately, our hero still can’t turn. Considering that these aliens love to chew on back hair (they truly are alien), the space marine needs to duck, spin and weave.

During gameplay, you also want the marine to shoot in the direction of the mouse pointer. Seems simple enough, right?

Fundamentally, it’s a tricky problem; the game exists in 3D space but the mouse exists in 2D. The solution is to convert the mouse pointer to 3D space, then determine where it’s pointing. It sounds like a complicated process, but it’s effortless with raycasting.

Note: This tutorial is part of a collection that teaches Unity development from the ground up. You can read the entire series over here. This series is free and does not require any account creation. All assets are provided. If you find it useful, feel free to buy me a coffee.

Understanding raycasts

Raycasting shoots an invisible ray from a target to a destination, and it sends a notification when it hits a GameObject. You’ll likely encounter a lot of GameObjects that intersect with a ray. Using a mask lets you filter out unwanted objects.

Raycasts are incredibly useful and serve various purposes. They’re commonly used to test if another player was struck by a projectile, but they can help you test if there’s geometry underneath a mouse pointer, as you’ll soon see.

The following image shows a raycast from a cube to a cone. Since the ray has a cone mask on it, it ignores the iconsphere layer and reports a hit to the cone.

This shows a picture of a GameObject firing a ray to a cone. It passes through another object.

The person shown in this diagram is actually Ray Wenderlich. Ray is the founder of Kodeco, the publisher of this book. Here, Ray is representing a raycast.

Drawing a ray

You’ll cast a ray from the camera to the mouse pointer. Then the space marine will turn to wherever the ray hits the floor, essentially following the mouse.

Open PlayerController.cs and add the following instance variables:

[SerializeField]
private LayerMask layerMask;
private Vector3 currentLookTarget = Vector3.zero;

The LayerMask lets you indicate what layers the ray should hit. currentLookTarget is where you want the marine to stare. Since you don’t know where to look at the start of the game, you set the value to zero.

Note: As you add instance variables, your serialized fields will jumble with your private fields. A best practice is to group them together. Keep all your serialized fields separate from your plain old private fields.

Next, add the following to FixedUpdate() underneath the existing code:

RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

First, this creates an empty RaycastHit. If you get a hit, it’ll be populated with an object. Second, you actually cast the ray from the main camera to the mouse position.

Rays are invisible, so they are hard to debug. You might think they will hit an object, but you won’t know until you actually see it hit something. Add the following code:

Debug.DrawRay(ray.origin, ray.direction * 1000, Color.green);

This will draw a ray in the Scene view while you’re playing the game.

Return to Unity and run your game. Look in the Scene view while moving your mouse in the Game window. You should see the ray as a green line.

This shows a ray being drawn on the screen.

Now you are ready to cast a ray. Delete the Debug.DrawRay() line.

Casting a ray

Return to PlayerController.cs, and add this code where you left off in FixedUpdate():

if (Physics.Raycast(ray, out hit, 1000, layerMask, QueryTriggerInteraction.Ignore)) 
{

}

These two lines of code do quite a bit:

  • Physics.Raycast actually casts the ray.
  • First, you pass in the ray that you generated along with the hit. Since the hit variable is marked as out, it can be populated by Physics.Raycast().
  • 1000 indicates the length of the ray. In this case, it’s a thousand meters.
  • The layerMask lets the cast know what you are trying to hit.
  • QueryTriggerInteraction.Ignore tells the physics engine not to activate triggers.

Add the following code inside the braces:

if (hit.point != currentLookTarget) 
{
  currentLookTarget = hit.point;
}

The hit.point comprises the coordinates of the raycast hit — it’s the point where the hero should look. If it’s different, perhaps because the player moved the mouse, then currentLookTarget will be updated.

Add the following after the previous bit of code:

// 1
Vector3 targetPosition = new Vector3(hit.point.x, transform.position.y, hit.point.z);
// 2
Quaternion rotation = Quaternion.LookRotation(targetPosition - transform.position);
// 3
transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * 10.0f);

This is where you turn the space marine. Here is the breakdown:

  1. You get the target position. Notice that the y-axis is derived from the marine’s position because you want the marine to look straight ahead instead of at the floor. If you used the floor, then the marine would rotate downwards.
  1. You calculate the current quaternion, which is used to determine rotation. You can learn all about quaternions in the Appendix, “Unity API.” To find the target quaternion, you subtract the targetPosition from the current position. Then you call LookRotation(), which returns the quaternion for where the marine should turn.
  1. Finally, you do the actual turn by using Lerp(). Remember, Lerp() is used to change a value (such as rotation in this place) smoothly over time. To learn more about Lerp(), check out the Appendix, “Unity API.”

Save your changes and go back to Unity. In the Hierarchy, select the SpaceMarine GameObject. Look for the Player Controller script in the Inspector, and select Floor in the drop-down.

This shows the floor being assigned to the Layer Mask.

The last thing to do is assign the floor physics layer to the, er, floor. In the Hierarchy, expand the BobbleArena and select World_Centre. In the layer drop-down, select floor.

Play your game like you mean it.

This shows the marine firing his gun while turning.

Now, the marine follows your mouse cursor and turns on a dime. Now you’re playing with power! :]

Where to go from here

Wow! That was quite a chapter! As you can see, the physics engine has a stockpile of features and does a lot of cool stuff. You can download the completed project for this chapter here:

https://drive.google.com/file/d/1dx6ZJ00Huokje2ZQLviMja0sLX-8zARO/view?usp=drive_link

In this chapter, you learned about:

  • Character Controllers and how they help simplify your code.
  • Collisions and Layers for when you want to collide with another object or opt out of collisions entirely.
  • Joints and how to put them in practice.
  • Raycasting and how to use rays to increase interactivity.

There’s a lot more to do in Bobblehead Wars. So far, you have just one alien, and he’s little more than a creepy-crawly couch potato.

In the next chapter, Chapter 5, “Managers and Pathfinding,” you’ll make a horde of aliens and unleash them upon the marine. He better stay sharp and hope that raycasting is working right! You’ll do this in the next tutorial.


Discover more from Jezner Blog

Subscribe to get the latest posts sent to your email.

By Brian Moakley

Brian Moakley is a writer and editor who lives amongst the quiet hills in New England. When not reading tales of high adventure, he is often telling such stories to all who will listen.

Related Post

Leave a Reply