CameraRaycaster.cs Explanation (Plus a Little Feedback)

Since this lesson introduced a number of new concepts for someone whose sole experience with Unity and C# is the Complete Unity Developer course, I just wanted to go back through the explanation (without referencing the video) to make sure I’m understanding. All of the comments will be my explanations of the code.

using UnityEngine;

public class CameraRaycaster : MonoBehaviour
{
    // Delcare an array from the Layer enum in Utiliy.cs. Each layer is set to
    // the integer value from Edit > Project Settings > Tags and Layers >
    // Layers for the layers that we want to detect with raycasting. These
    // are going to be the layers we want to try and detect.
    public Layer[] layerPriorities = {
        Layer.Enemy,
        Layer.Walkable
    };

    // The max distance from the camera to look for objects before giving up.
    float distanceToBackground = 100f;
    // This will be set to the Main Camera object in Start().
    Camera viewCamera;

    /*
     m_hit is a private member RaycastHit variable and is not accessible outside
     of CameraRaycaster.

     When another external method calls hit, the get property simply returns the
     value of m_hit, which is set in the Update() method. This lets me make a
     variable publicly readable, but protects it from being changed outside the
     method.

     If I wanted to set m_hit from outside of CameraRaycaster, I could define a
     set { m_hit = value } and access it later the way I would with a normal
     variable declaration.
    */
    RaycastHit m_hit;
    public RaycastHit hit
    {
        get { return m_hit; }
    }

    /*
      This does the same thing as hit, but returns the Layer enum property set
      in the Update() method.
    */
    Layer m_layerHit;
    public Layer layerHit
    {
        get { return m_layerHit; }
    }

    void Start() // TODO Awake?
    {
        // Set viewCamera to Main Camera
        viewCamera = Camera.main;
    }

    void Update()
    {
        // Loop through each layer in layerPriorities array
        foreach (Layer layer in layerPriorities)
        {
            // Cast a ray and return the RaycastHit values if it hits an object that is part of
            // the given layer. (This is why object that aren't in the Walkable
            // or Enemy layers appear to are "invisible" to the ray. We just
            // don't filter for them. in RaycastForLayer())
            var hit = RaycastForLayer(layer);
            // if hit is not null...
            if (hit.HasValue)
            {
                // ... set m_hit to the value (all the infor for) hit...
                m_hit = hit.Value;
                //... and set m_layerHit to the Layer property we tested for...
                m_layerHit = layer;
                //... and get out of Dodge 'cause you're done.
                return;
            }
        }

        // If the hit didn't hit any layer we tested for, set the distance
        // propery to distanceToBackground...
        m_hit.distance = distanceToBackground;
        // ..and the layer to the property indicating that we didn't find
        // anything.
        m_layerHit = Layer.RaycastEndStop;
    }

    // Usually we must return a valid RaycastHit value, but the ? let's us
    // return null.
    RaycastHit? RaycastForLayer(Layer layer)
    {
        // Set up a LayerMask using a bitwise operation set equal to our passed
        // in layer enum value.
        int layerMask = 1 << (int)layer;
        // Shoot a ray that originates from the main camera, passes through the
        // position of the mouse cursor (see illustration) and tries to impact
        // against something.
        Ray ray = viewCamera.ScreenPointToRay(Input.mousePosition);

        // Declare the variable we're going to be setting to the value of the
        // object the raycast hit. Out variable 'cause "Unity".
        RaycastHit hit;

        // If ray hits layerMask before going distanceToBackground, assign it to
        // hit
        bool hasHit = Physics.Raycast(ray, out hit, distanceToBackground, layerMask);

        // If hit has a value, return it.
        if (hasHit)
        {
            return hit;
        }

        // If hit does not have a value, return null. Because we can. The
        // all-knowing question mark says so.
        return null;
    }
}

The following illustration shows how viewCamera.ScreenPointToRay(Input.mousePosition) casts a ray from the camera through the cursor position (shows two rays (red) casting through two mouse positions (green) from the camera (blue))

Does that look correct?

Some feedback: I’m all for dropping in code when it’s stuff we’ve already covered, but it’s very much helpful to write code for new concepts. I understand it better because each block is being explained as we write. It helps me relate one section back to the whole.

2 Likes

Thanks for this Justin.
I had to drop my work on this course due to other priorities and am just coming back to it today.
I can follow your logic from the comments you made on the script so I would say you are correct in your statements.

Your comment, “it’s very much helpful to write code for new concepts” is also one of my main frustrations. For something new and ‘key’ to the design such as this it would be good to see the code blocks built up bit by bit, tested, and expanded out to create the script so that we can understand the need for each line of code.

1 Like

Privacy & Terms