Dot, Cross & Reflect

Three small tools for working with directions: Dot ("is it in front of me?"), Cross ("which way is sideways?") and Reflect ("which way does it bounce?").

Think of a game of pool. Dot is asking "how much is the ball heading toward that pocket?" Cross is finding the direction that points straight up out of the table. Reflect is the ball bouncing off a cushion.

First: getting a direction

Most of these tools need a direction, an arrow with length 1. To get one from two positions, subtract and normalize:

Vector3 toTarget = target.position - transform.position; // arrow from me to target
float distance   = toTarget.magnitude;                   // how long the arrow is
Vector3 dir      = toTarget.normalized;                  // same arrow, length 1

Remember: target minus me points from me to the target.

Dot: how much do two arrows agree?

Vector3.Dot(a, b) gives back one number. If both arrows have length 1:

Dot resultMeaning
1Same direction
0.5About 60° apart
0At a right angle (sideways)
-1Opposite directions (behind)

So "is the player in front of me?" is just Dot(transform.forward, dirToPlayer) > 0.

Example: a guard's view cone

Attach to a guard, assign a target and watch the line in the Scene view: green = seen, red = not seen.

using UnityEngine;

public class ViewCone : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField, Range(0, 180)] private float viewAngle = 90f; // total cone width
    [SerializeField] private float range = 10f;

    void Update()
    {
        if (target == null) return;

        Vector3 toTarget = target.position - transform.position;
        if (toTarget.magnitude > range) { Draw(false); return; }

        // cos(half the angle) is the smallest Dot that still counts as "inside"
        float minDot = Mathf.Cos(viewAngle * 0.5f * Mathf.Deg2Rad);
        bool seen = Vector3.Dot(transform.forward, toTarget.normalized) >= minDot;
        Draw(seen);
    }

    void Draw(bool seen) =>
        Debug.DrawLine(transform.position, target.position, seen ? Color.green : Color.red);
}

The cone doesn't know about walls. Add a Physics.Raycast if walls should block sight.

Cross: the arrow at right angles to both

Vector3.Cross(a, b) gives an arrow that is perpendicular to both a and b. Lay two pencils flat on a desk; their cross product points straight up out of the desk (or straight down, if you swap the order).

UseCode
"Right" from a forward directionVector3.Cross(Vector3.up, forward)
The way a triangle faces (its normal)Vector3.Cross(b - a, c - a).normalized
Is the target to my left or right?Vector3.Cross(transform.forward, dir).y: positive = right, negative = left

If the two arrows point along the same line, the result is zero: there's no single "sideways" answer.

Reflect: bouncing

Vector3.Reflect(direction, normal) returns the direction after a perfect bounce. The normal is the arrow sticking straight out of the surface; a raycast hit gives it to you as hit.normal.

incomingreflectednormalsurface
The sideways part of the motion stays the same; the part going into the wall flips.

Example: a bouncing laser preview

using UnityEngine;

public class BouncePreview : MonoBehaviour
{
    [SerializeField] private int maxBounces = 4;
    [SerializeField] private float maxDistance = 30f;

    void Update()
    {
        Vector3 origin = transform.position;
        Vector3 dir = transform.forward;

        for (int i = 0; i <= maxBounces; i++)
        {
            if (!Physics.Raycast(origin, dir, out RaycastHit hit, maxDistance))
            {
                Debug.DrawRay(origin, dir * maxDistance, Color.yellow);
                break;
            }
            Debug.DrawLine(origin, hit.point, Color.yellow);
            dir = Vector3.Reflect(dir, hit.normal);
            origin = hit.point + hit.normal * 0.01f; // step off the wall so we don't hit it again
        }
    }
}

Bonus: sliding along a wall

Reflect bounces. To slide along a wall instead (like a character pushing into it), remove the part of the movement that goes into the wall:

Vector3 slide = Vector3.ProjectOnPlane(velocity, hit.normal);

Common Mistakes

See also