Feedback on this lesson and fully commented CameraRaycaster.cs

I think this one was quite steep even for the “developers” among us. Even though we can just copy and paste CameraRaycaster.cs, I don’t think it’s a good idea to give people a weapon they don’t know the inner workings of, and the explanation about the code (though long) is still way too short for people who are not familiar with all the stuff used. Basically you assume we know the type of RaycastHit, the type of Ray, getters and setters, out parameters, nullable parameters and even if you know how these work, this script is still pretty complicated.

I don’t give up easily so I spent about three hours Googling, but I’m still not sure whether I understand everything (I did complete the challenge though).

Here is my commented version of the script after having done my homework. If you see anything funny in it that clearly indicates I have no clue what’s going on, please let me know!

using UnityEngine;

public class CameraRaycaster : MonoBehaviour
{
    // Define the priority order of the layers that have been defined by enum Layer in Utility.cs
    public Layer[] layerPriorities = {
        Layer.Enemy,
        Layer.Walkable
    };

    // 100f = 100 meters. If beam distance becomes longer than that, just give up.
    // SerializeField exposes the value in the Inspector without us being to change it.
    [SerializeField] float distanceToBackground = 100f;
    // Define a camera (which camera is not known yet).
    Camera viewCamera;

    /* RaycastHit
    barycentricCoordinate   The barycentric coordinate of the triangle we hit.
    collider                The Collider that was hit.
    distance                The distance from the ray's origin to the impact point.
    lightmapCoord           The uv lightmap coordinate at the impact point.
    normal                  The normal of the surface the ray hit.
    point                   The impact point in world space where the ray hit the collider.
    rigidbody               The Rigidbody of the collider that was hit. If the collider is not attached to a rigidbody then it is null.
    textureCoord            The uv texture coordinate at the collision location.
    textureCoord2           The secondary uv texture coordinate at the impact point.
    transform               The Transform of the rigidbody or collider that was hit.
    triangleIndex           The index of the triangle that was hit.
    */
    RaycastHit m_hit;
    // So-called getter setter, used to set properties without manipulating them the wrong way. This seems to be a convention for RaycastHit. So as for now, this is just a complicated way to say:
    // hit = m_hit (a RaycastHit)
    public RaycastHit hit
    {
        get { return m_hit; }
    }

    // layerHit = m_layerHit (a Layer)
    Layer m_layerHit;
    public Layer layerHit
    {
        get { return m_layerHit; }
    }

    void Start() // TODO Awake?
    {
        // That's our Main Camera tagged as MainCamera.
        viewCamera = Camera.main;
    }

    void Update()
    {
        // Look for and return priority layer hit.
        // We currently have three layers in the enum (Walkable, Enemy and RaycastEndstop), of which only two are used in this class: Walkable and Enemy 
        // (RaycastEndstop is defined in the enum, but not in layerPriorities).
        foreach (Layer layer in layerPriorities)
        {
            // Give us RaycastHit hit for each layer. First Walkable, then Enemy in that order, as defined in the Layer array.
            // The last layer hit will define the final value, so Walkable takes precedence over Enemy (walk over stuff even if enemy is in front of it).
            var hit = RaycastForLayer(layer);
            // If we hit the layer...
            if (hit.HasValue)
            {
                // RaycastHit m_hit takes its value from the succesful Raycasthit.
                m_hit = hit.Value;
                // Layer m_layerHit takes its value from the layer we tested.
                m_layerHit = layer;
                // That we return.
                return;
            }
        }

        // Otherwise return background hit (and define distance).
        m_hit.distance = distanceToBackground;
        m_layerHit = Layer.RaycastEndStop;
    }

    // The question mark (a so-called nullable parameter) enables us to return null (normally not allowed for RaycastHits).
    // This is a method called RaycastForLayer which gives us a RaycastHit as output, which may also be null.
    RaycastHit? RaycastForLayer(Layer layer)
    {
        // For Walkable       we bitshift the number 1 eight     times, so that 0000.0000.0000 becomes 0000.1000.0000.
        // For Enemy          we bitshift the number 1 nine      times, so that 0000.0000.0000 becomes 0001.0000.0000.
        // (In reality there are more digits).
        // We do nothing with RaycastEndstop (we never hit it), as it's not defined in the Inspector's layers. Instead, if we don't hit anything, this will automatically be converted to RaycastEndstop
        // by the method calling this method in Update().
        // The layer is input in this method, and the resulting mask is put in layerMask.
        int layerMask = 1 << (int)layer; // See Unity docs for mask formation
        /* Ray
        direction	The direction of the ray (a normalized Vector3).
        origin	    The origin point of the ray (Vector3).
        */
        // Returns a ray going from camera through a screenpoint (in this case the current mousePosition).
        // So ray now contains the direction of the ray from the camera to the viewpoint, and the camera as origin.
        Ray ray = viewCamera.ScreenPointToRay(Input.mousePosition);

        RaycastHit hit; // used as an out parameter. Must be defined in advance.
        /* Physics.Raycast
        origin	                The starting point of the ray in world coordinates.
        direction	            The direction of the ray.
        maxDistance	            The max distance the ray should check for collisions.
        layerMask	            A Layer mask that is used to selectively ignore Colliders when casting a ray.
        queryTriggerInteraction	Specifies whether this query should hit Triggers.
        */
        // ray contains origin and direction already: cast from camera to mouse position. So no need to input them to Physics.Raycast separately as origin, direction.
        // The result is put in the Raycasthit hit via out. The actual result is a bool: True if the ray intersects with a Collider (so the object in question must have a collider!), otherwise False.
        // Ben probably dislikes out because Physics.Raycast outputs a bool, but also hit behind the screens. 
        // In PHP we'd probably use list(hasHit, hit) = method(), where method() exits with return array(hasHit, hit), giving much more clarity.
        bool hasHit = Physics.Raycast(ray, out hit, distanceToBackground, layerMask);
        // If we hit the given layer, then return the Raycasthit hit.
        if (hasHit)
        {
            return hit;
        }
        // If we did not hit the given layer, then return null.
        return null;
    }
}
2 Likes

commenting code and then some :slight_smile:
fair play to your googling skills, your comments will make it easier for folks like me to do some further reading on Raycaster.

1 Like

Thanks for the comments! Although I’m still having a really hard time in this episode, is it ok to not understand the details of the scripts?

I’d say it’s expected, as I think Ben goes way too fast in this episode… but I don’t think it’s okay, as you will need this knowledge later on. So read those comments, give yourself a few days, tinker and experiment with them with Print comments and see how everything works!

1 Like

Privacy & Terms