Lesson 7 - Vector3 Math and Direction Vectors

Destination minus start, normalized: how to compute a direction, why the subtraction order decides between chasing and fleeing, what normalizing does and does not change, and how the dot product answers 'is it in front of me'.

Subject: Unity Game Engine · 60 slides · code lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. Vector3 Math and Direction Vectors

Title

Unity - Lesson 7

Destination minus start, normalized. Four lines that move anything towards anything.

2. What you will be able to do

Objectives

The diagnostic scored 1 out of 3 here, and the one correct answer was marked a guess. All three recorded misconceptions are fixable in one sitting, and every number in this deck is checkable on paper.

  1. Say which axis is up in Unity, without hesitating
  2. Write the direction from A to B and explain why the order is that way round
  3. Say exactly what .normalized changes and what it leaves alone
  4. Move an object towards a target at a constant speed, frame-rate independently
  5. Use Vector3.Dot to answer 'is it in front of me' without computing an angle

3. Answer these three from memory

Warm-up

No looking. These are the exact three the diagnostic tested.

Discussion prompt

1) Which axis is up in Unity? 2) To get a vector from A to B, do you write A - B or B - A? 3) Does normalizing a vector change its direction?

Hint: The second one has a rule you already know from arithmetic: displacement is destination minus start.

Answer:

  1. Y is up. (0, 1, 0). Z is forward/depth. Blender and most CAD tools use Z for up, which is where the confusion is imported from.
  2. B - A gives the vector from A to B: destination minus start.
  3. No. Normalizing divides all three components by the same number, which changes the length to 1 and cannot turn the arrow.

If any of those was a guess, that is exactly what the diagnostic saw - and each one is about to become something you can derive rather than recall.

4. Points, directions and axes

Section

Part 1

5. Unity is Y-up

Concept

Figure (svg): Unity's coordinate axes drawn with Y pointing up, Z forward into the scene and X to the right

Unity is Y-up. Z is depth, not height.

Vector3.up is (0, 1, 0). Vector3.forward is (0, 0, 1). Vector3.right is (1, 0, 0). Height is Y, and Z is depth.

This matters because most other 3D software is Z-up, so a model imported from Blender arrives lying on its face until the import settings are corrected - and 'add height' written as Z is a bug that moves things sideways.

Unity Manual - Positioning GameObjects (coordinate system) coordinate system

6. The same three numbers, two meanings

Concept

A Vector3 is three floats. What it means depends entirely on how you are using it.

used asmeansexample
a positiona place in the worldtransform.position
a directionwhich way, and how fartarget.position - transform.position
a direction onlywhich way, length 1offset.normalized
a scalea multiplier per axistransform.localScale

Nothing in the type tells you which. This is why naming matters more here than almost anywhere else: toTarget and targetPos are both Vector3 and mixing them up compiles perfectly.

Unity Scripting API - Vector3 Vector3

7. Predict: which is up?

Prediction

You want an object to hover one unit above its current position.

Predict first

Which line does that in Unity?

  • transform.position += new Vector3(0, 0, 1);
  • transform.position += new Vector3(0, 1, 0);
  • transform.position += Vector3.forward;
  • transform.position += Vector3.one;

Correct: new Vector3(0, 1, 0) - which is also Vector3.up.

Why: Y is height in Unity. The first and third options both move the object one unit deeper into the scene along Z, which looks like nothing at all from the default camera - and that silent non-effect is exactly why the z-is-up habit survives so long.

8. Match the constant to its value

Matching

Match the pairs

What is each one?

  • v1. Vector3.up
  • v2. Vector3.forward
  • v3. Vector3.right
  • v4. Vector3.one
  • r1. (0, 1, 0)
  • r2. (0, 0, 1)
  • r3. (1, 0, 0)
  • r4. (1, 1, 1)

Why: Vector3.one is the odd one out: it is not a direction at all, it is the multiplier you use for uniform scale - transform.localScale = Vector3.one * 2f. Using it as a direction gives you a diagonal of length 1.73, which is rarely what anyone meant.

9. The whole vocabulary, on one object

Worked example

Five operations, and what each one gives you.

Vector3 me = transform.position;              // (0, 0.5, 0)
Vector3 them = target.position;               // (3, 0.5, 4)

Vector3 offset   = them - me;                 // (3, 0, 4)   the arrow to them
float   distance = offset.magnitude;          // 5.00        how far
Vector3 direction= offset.normalized;         // (0.6, 0, 0.8) which way, length 1
float   cheap    = offset.sqrMagnitude;       // 25.00       no square root
float   sameThing= Vector3.Distance(me, them);// 5.00        readable alias
expressionvalue heretypeuse it for
them - me(3, 0, 4)Vector3the arrow between two points
.magnitude5.00floatdistance
.normalized(0.6, 0, 0.8)Vector3pure direction
.sqrMagnitude25.00floatcomparing distances cheaply
Vector3.Distance(a, b)5.00floatthe same thing, more readable

Check the arithmetic yourself

Why: 3 squared plus 4 squared is 25, and the square root is 5. Then (3, 0, 4) divided by 5 is (0.6, 0, 0.8). Every number in this deck is checkable like that.

10. Why is sqrMagnitude cheaper?

Explain it to yourself

Discussion prompt

sqrMagnitude skips the square root. Explain when that is a legitimate optimisation and when it would be a bug.

Answer:

Legitimate when you only compare: if (offset.sqrMagnitude < 25f) is exactly if (distance < 5f), because squaring preserves order for non-negative numbers. Compare against the threshold squared.

A bug when you use the number itself - as a speed, a lerp fraction, a displayed distance. A sqrMagnitude of 25 is not 25 metres, and a UI that shows it will be wrong in a way that looks plausible.

11. Position, direction, or neither?

Sorting

Sort each expression by what it means.

Sort into buckets

What kind of Vector3 is each?

A point in space
transform.position
A direction
target.position - transform.position; (target.position - transform.position).normalized
Neither - a per-axis multiplier
transform.localScale
point
It names a place. Adding two positions together is meaningless - 'London plus Paris' is not a location - which is a useful check on any line that adds two of these.
dir
It is a displacement: which way and, unless normalized, how far. Directions add sensibly and can be scaled by a speed.
other
A scale is three independent multipliers, one per axis. It looks like the others and behaves like none of them - Vector3.one is 'no change', not 'a direction'.

12. Destination minus start

Section

Part 2

13. The arrow from A to B is B minus A

Concept

Figure (svg): Two arrows between the same pair of points showing that target minus me points one way and me minus target points the other

Destination minus start. Swap them and you have written a flee script.

Subtraction gives the displacement from the second operand to the first. So target - me points at the target, and me - target points away from it.

The mnemonic worth keeping: destination minus start, the same rule as 'final minus initial' in any change calculation.

14. Predict the chase

Prediction

A seeker at the origin, a target at (3, 0, 4). The script computes Vector3 dir = (transform.position - target.position).normalized; and moves along dir.

Predict first

What does the seeker do?

  • Moves to the target
  • Moves directly away from the target
  • Circles the target
  • Stays still

Correct: Moves directly away - it flees.

Why: The operands are swapped, so the arrow points from the target to the seeker, and moving along it increases the distance. Nothing errors, nothing is null, and the object moves smoothly in exactly the wrong direction - which is why this bug is usually found by watching rather than by reading.

15. Move towards, in four lines

Worked example

This is the whole pattern, and it does not get more complicated in a real game.

void Update()
{
    Vector3 offset    = target.position - transform.position;   // TO the target
    float   distance  = offset.magnitude;
    if (distance < 0.6f) return;                                // arrived

    Vector3 direction = offset.normalized;
    transform.position += direction * (speed * Time.deltaTime);
}

Line 8 is where speed becomes distance

Why: direction has length 1, so multiplying by speed x deltaTime moves exactly that many metres this frame - no more, no less, on any machine.

timedistance to targetdirectionmoved this frame at speed 2, 60 fps
0.0s5.00(0.60, 0.00, 0.80)0.033
1.0s3.00(0.60, 0.00, 0.80)0.033
2.0s1.00(0.60, 0.00, 0.80)0.033
2.2s0.57-stops: arrived

The direction column never changes, because the target is not moving and the seeker is travelling in a straight line at it. That constancy is what makes normalizing worth doing.

16. Find the two bugs

Error analysis

A chase script. Two lines are wrong, in two different ways.

Annotate

  • Bug one: the operands are the wrong way round, so this points away from the target. Should be target.position - transform.position.
  • Bug two: dir is not normalized, so its length is the DISTANCE. The seeker starts fast when far away and slows as it approaches - the opposite of constant speed.
  • Bug three, arguably: no Time.deltaTime, so speed means 'per frame'. On a 144 fps machine it moves 2.4 times as fast as on 60 fps.

Three fixes, one line each: swap the operands, add .normalized, multiply by Time.deltaTime. The result is the four-line pattern from the previous slide.

17. Trap: subtracting the wrong way round

Trap

The trap

Both objects have a position, so subtract them and you get the direction between them.

Vector3 toTarget = transform.position - target.position;
transform.position += toTarget.normalized * speed * Time.deltaTime;
what you getwhy
the enemy runs awaythe arrow points target -> me
no errorthe maths is perfectly valid
it looks deliberatefleeing is a real behaviour, so nothing looks broken

The fix

Name the variable after what it is, and the order writes itself.

Vector3 toTarget = target.position - transform.position;   // TO the target
transform.position += toTarget.normalized * speed * Time.deltaTime;
variable namethe expression it forces
toTargettarget.position - transform.position
awayFromTargettransform.position - target.position
dirnothing - which is why dir is a bad name here

18. Which order for which behaviour?

Discrimination

Sort each behaviour by which subtraction it needs.

Sort into buckets

target - me, or me - target?

target - me (towards)
An enemy chases the player; A turret aims at a target
me - target (away)
A frightened animal flees the player; An explosion pushes debris outward from its centre
to
You want the arrow that points at the other thing: chasing, aiming, reaching. Destination minus start.
away
You want the arrow pointing outwards from the other thing: fleeing, blast knockback, repulsion. Note that for the explosion, 'me' is the piece of debris and 'target' is the blast centre - so the operands are still named consistently.

19. Complete the flee

Fill the middle

A rabbit runs from the player, at a constant speed.

Fill in the blanks

void Update()
-} player.position;
transform.position += away.normalized * (speed * Time.deltaTime);
}

Why: Fleeing is the same code as chasing with the operands swapped - which is why naming the variable away rather than dir is what stops the bug. And without normalized the rabbit would accelerate the further it got, since the vector's length is the distance.

20. Normalizing: length 1, same way

Section

Part 3

21. Divide every component by the length

Concept

Figure (svg): One long arrow and one short arrow along exactly the same line, showing normalizing changes length only

Dividing all three components by the same number cannot turn the arrow.

v.normalized divides each of the three components by the vector's magnitude. The result has length exactly 1 and points exactly where the original pointed.

(3, 0, 4) has length 5, so normalized is (3/5, 0/5, 4/5) = (0.6, 0, 0.8). Check: 0.6 squared plus 0.8 squared is 0.36 + 0.64 = 1.

Unity Scripting API - Vector3.normalized normalized

22. Predict: does it turn?

Prediction

Vector3.Angle(offset, offset.normalized) measures the angle between a vector and its own normalized version.

Predict first

What does it return?

  • 0 degrees
  • 90 degrees
  • 45 degrees
  • It depends on the vector

Correct: 0 degrees, always.

Why: Dividing all three components by the same positive number scales the arrow without turning it - the ratio between x, y and z is untouched, and that ratio IS the direction. This is the third misconception from the diagnostic, and the lab prints this exact check as a PASS line.

23. A map scale, not a compass

Intuition

Normalizing is redrawing an arrow at a standard length. The bearing is identical; only the number of centimetres on the page changed.

Which is why the pattern is always direction * speed: take the pure bearing, then decide separately how fast to travel along it. Skipping the normalize means the distance to the target leaks into the speed.

24. What happens if you forget it

Worked example

The same seeker, with and without .normalized, both at speed 2.

// A: normalized - constant speed
transform.position += offset.normalized * (speed * Time.deltaTime);

// B: not normalized - speed scales with distance
transform.position += offset * (speed * Time.deltaTime);
distance to targetA: metres per secondB: metres per second
10.02.020.0
5.02.010.0
1.02.02.0
0.12.00.2

B is not broken - it is an ease-out, and it is sometimes exactly what you want for a camera or a UI element. The bug is using it without meaning to, and then wondering why the enemy crawls the last metre.

25. What about a zero vector?

Edge cases

Discussion prompt

The seeker is exactly on top of its target, so offset is (0, 0, 0). What does .normalized return, and what should your code do about it?

Hint: Dividing by zero in floating point does not throw.

Answer:

Unity returns (0, 0, 0) rather than a NaN - it checks for a near-zero length and gives you zero back. So nothing crashes, and the object simply does not move.

But Quaternion.LookRotation(Vector3.zero) does complain, and any code that assumes a length of 1 gets a length of 0. The habit: guard with if (offset.sqrMagnitude > 0.0001f) before using a direction for rotation.

26. Trap: normalize then multiply by distance... twice

Trap

The trap

I want to move a fraction of the way there, so I will normalize and scale by the distance.

Vector3 offset = target.position - transform.position;
Vector3 step = offset.normalized * offset.magnitude * 0.1f;
transform.position += step;

normalized * magnitude is just the original vector again, so this is offset * 0.1f written the long way - and the speed still scales with distance.

The fix

Decide which you want: constant speed, or a constant fraction. They are different behaviours with different code.

// constant speed - arrives at a predictable time
transform.position = Vector3.MoveTowards(
    transform.position, target.position, speed * Time.deltaTime);

// constant fraction - eases in, never quite arrives
transform.position = Vector3.Lerp(
    transform.position, target.position, 0.1f);
helperbehaviourreaches the target?
MoveTowardsconstant speed, clamped so it cannot overshootyes, exactly
Lerp towards a moving valueeases out, slows as it closesasymptotically - never exactly
Slerpsame, but along an arc - for directions and rotationsas above

27. Complete the operations table

Comparison

Comparison matrix

ReturnsLengthUse for
b - aVector3the distancethe arrow from a to b
(b - a).normalizedVector31pure direction
(b - a).magnitudefloat-how far apart

28. Two of these are true

Two truths and a lie

Eliminate the wrong options

Which claim about normalizing is false?

  • A. Normalizing a zero-length vector returns zero rather than throwing.
  • B. Normalizing can change which way a vector points.
  • C. v.normalized leaves v itself unchanged; v.Normalize() modifies it in place.

Survives elimination: B

Why: All three components are divided by the same positive number, so their ratios - which are the direction - are untouched. Only the length changes. That is the misconception the diagnostic recorded, and the lab asserts it with Vector3.Angle every run.

29. Complete the constant-speed move

Faded example

Fill in the blanks

void Update()
magnitude} < arriveDistance) return;

transform.position += toTarget.normalized * (speed * Time.deltaTime);
}

Why: magnitude compared against a threshold is how you ask 'am I there yet?'. Time.deltaTime turns speed from per-frame into per-second, which is the whole reason the object moves at the same real speed on a 30 fps laptop and a 144 fps desktop.

30. The dot product

Section

Part 4

31. Is it in front of me?

Concept

Vector3.Dot(a, b) multiplies matching components and adds them up. When both vectors are normalized, the result is the cosine of the angle between them.

Vector3 toTarget = (target.position - transform.position).normalized;
float facing = Vector3.Dot(transform.forward, toTarget);
// facing >  0   ahead
// facing == 0   exactly to the side
// facing <  0   behind
// facing ==  1  dead ahead
dotanglemeaning
1.000 degreesdead ahead
0.8730 degreesahead and a bit off
0.0090 degreesexactly to the side
-0.50120 degreesbehind
-1.00180 degreesdirectly behind

Unity Scripting API - Vector3.Dot Vector3.Dot

32. How much of one arrow lies along the other

Intuition

Picture a torch shining straight down onto the second arrow. The dot product is the length of the first arrow's shadow on it.

Point the same way and the shadow is the full length (dot 1). Point at right angles and the shadow has no length at all (dot 0). Point the other way and the shadow falls behind the start (negative).

That is why the sign answers 'in front or behind' before any angle is computed - and why sign is usually all you need.

33. A field-of-view test, without trigonometry

Worked example

A 60-degree cone means 30 degrees either side of forward, so the threshold is cos(30) = 0.866.

float threshold = Mathf.Cos(fovDegrees * 0.5f * Mathf.Deg2Rad);   // 0.866 for 60
Vector3 toTarget = (target.position - transform.position).normalized;
bool visible = Vector3.Dot(transform.forward, toTarget) > threshold;
angle to targetdotinside a 60-degree cone?
9 degrees0.988yes
26 degrees0.899yes
35 degrees0.819no
89 degrees0.017no
137 degrees-0.731no

The threshold is computed once

Why: cos(30) does not change while the game runs, so it belongs in Start, not in Update - the same 'searching costs, doing does not' habit from lesson 5.

34. Predict the sign

Prediction

A guard faces world +Z. A player stands directly behind them.

Predict first

What does Vector3.Dot(guard.forward, toPlayer) return, roughly?

  • About 1
  • About 0
  • About -1
  • It depends on the distance

Correct: About -1.

Why: Directly behind means 180 degrees, and cos(180) is -1. Distance does not appear because both vectors are normalized - which is exactly why the test is about direction only, and why forgetting to normalize turns a clean -1 into some large negative number that no longer means an angle.

35. Cross product, in one slide

Concept

Vector3.Cross(a, b) gives a vector perpendicular to both. You need it less often, but when you do, nothing else will substitute.

wantuse
is it in front of me?Dot - sign of the result
is it to my left or my right?Cross, then check the sign of the Y component
the angle between two directionsVector3.Angle(a, b) - degrees, already computed
a normal to a surface from two edgesCross

The left/right test is the useful one: Vector3.Cross(transform.forward, toTarget).y is positive when the target is to your right and negative when it is to your left.

36. Dot, Cross, or Distance?

Discrimination

Sort into buckets

Which tool answers each question?

Distance / magnitude
Is the enemy within 10 metres?; How far do I still have to walk?
Dot product
Is the enemy in front of me?
Cross product
Should I turn left or right to face them?
dist
It is a question about how far apart two points are, so it is a length. Compare against a threshold, and use sqrMagnitude against the threshold squared if it runs every frame.
dot
It is a question about alignment - are these two directions pointing the same way. The sign alone answers front or behind; comparing against cos(half the cone) answers 'inside my field of view'.
cross
It is a question about sidedness, which needs an axis perpendicular to both directions. The Y component of the cross tells you which side, and turning towards a target is its classic use.

37. Decode a dot-product log

Notation

Annotate

  • Half the cone angle, not the whole cone. A 60-degree FOV allows 30 degrees either side of forward, and forgetting the halving is the commonest error in this calculation.
  • 0.987 and 9 degrees are the same fact twice: cos(9 degrees) = 0.987. The dot is the cheap form; the angle is the readable one.
  • 0.819 is BELOW the threshold even though it is comfortably positive - so the target is still in front, just outside the cone. Positive dot and 'visible' are not the same test.

38. Predict the next reading

Pattern

A watcher spins steadily. The dot readings so far: 0.99, 0.71, 0.02, -0.68.

Predict first

What comes next, roughly?

  • -0.99
  • 0.99
  • 0.02
  • 1.71

Correct: About -0.99 - the target is passing directly behind.

Why: The readings are tracking the cosine as the angle grows: 0 degrees, 45, 89, 133, and next around 180. The values must stay between -1 and 1 for normalized vectors, which is what rules out the last option - and a dot outside that range is a reliable sign that something was not normalized.

39. Build it in Unity

Section

Build

40. What you are about to build

Concept

A seeker that computes its own direction and walks it, a twin with the subtraction reversed, and a watcher that prints the dot product as a target swings through its field of view.

DirectionLab
offset, magnitude, normalized, move - with a switch to reverse it.
DotProductProbe
Vector3.Dot as a field-of-view test, with gizmos.

Positions are a 3-4-5 triangle on purpose: seeker at (0, 0.5, 0), target at (3, 0.5, 4). Files: unity-labs/Assets/NaruhodoLabs/Lesson07_Vectors/.

41. Do the arithmetic before Unity does

Step zero

Discussion prompt

Seeker at (0, 0.5, 0), target at (3, 0.5, 4). Work out the offset, the distance and the direction on paper, showing each step.

Answer:

  1. offset = target - me = (3 - 0, 0.5 - 0.5, 4 - 0) = (3, 0, 4)
  2. magnitude = square root of (9 + 0 + 16) = square root of 25 = 5
  3. normalized = (3/5, 0/5, 4/5) = (0.6, 0, 0.8)
  4. check: 0.6 squared + 0.8 squared = 0.36 + 0.64 = 1. Length 1, as promised

The y components cancelled because both objects are at the same height - which is why the offset has a clean zero in the middle.

42. Step 1: build the seeker

Worked example

A capsule, a sphere and one component.

  1. Sphere Target at (3, 0.5, 4)
  2. Capsule Seeker at (0, 0.5, 0), scale (0.6, 0.5, 0.6)
  3. Add Component > Direction Lab on the seeker; drag Target into its Target slot; speed 2
  4. Duplicate it as Fleer at (-2, 0.5, 0) and tick Reverse Subtraction
Console lineexpected value
offset(3.00, 0.00, 4.00)
magnitude (distance)5.00
normalized (direction)(0.60, 0.00, 0.80) length 1.00
angle between offset and normalized0.000 degrees

That last line is the misconception, checked

Why: The lab asserts it every run, so 'normalizing does not turn the vector' stops being something you were told and becomes something you watched.

43. Step 2: watch the gizmos

Worked example

Select the seeker before pressing Play and look at the Scene view, not the Game view.

void OnDrawGizmos()
{
    Gizmos.color = Color.cyan;
    Gizmos.DrawLine(transform.position, target.position);       // the offset

    Vector3 direction = (target.position - transform.position).normalized;
    Gizmos.color = Color.yellow;
    Gizmos.DrawRay(transform.position, direction);              // exactly 1 unit

    Gizmos.color = Color.green;
    Gizmos.DrawRay(transform.position, transform.forward * 1.5f);
}
colourwhat it drawswhat to notice
cyanthe full offsetits length shrinks as the seeker closes in
yellowthe normalized directionalways the same length - that is what length 1 looks like
greenthe seeker's own forwardswings round as LookRotation turns it

OnDrawGizmos runs in the editor, not in a build. It is the cheapest debugging tool in Unity and almost nobody uses it enough.

44. Commit before you run the fleer

Hypothesis

The Fleer has the same speed and the same target, with Reverse Subtraction ticked.

Predict first

What will its distance-to-target reading do over the first three seconds?

  • Shrink, then stop at the arrive distance
  • Grow steadily, and never stop
  • Stay the same - the two errors cancel
  • Shrink more slowly than the seeker's

Correct: Grow steadily, and never stop.

Why: The arrival check only runs for the non-reversed case, and the fleer is moving away, so the distance rises forever. Watching a smooth, confident, completely wrong behaviour is the point of this experiment - the bug produces no error and no hesitation.

45. Where this shows up in a real game

Real world

Discussion prompt

An enemy should chase the player, but only when the player is in front of it and within 15 metres. Write the three checks in order, and say which vector tool each one uses.

Answer:

  1. In range: (player.position - transform.position).sqrMagnitude < 225f - squared, so no square root every frame. 15 squared is 225.
  2. In front: normalize the offset, then Vector3.Dot(transform.forward, toPlayer) > 0.5f for a 120-degree cone.
  3. Then chase: transform.position += toPlayer * (speed * Time.deltaTime) with toPlayer already normalized.

Order matters for cost as well as logic: the cheapest test goes first, so most frames stop at line one and never compute a direction at all.

46. Explain normalizing in two sentences

Explain it

Discussion prompt

A teammate asks why everyone writes .normalized before multiplying by speed. Answer in two sentences, without the word 'magnitude'.

Answer:

Model answer: 'The arrow between two objects is as long as the gap between them, so if you multiply it by speed the object moves faster when it is further away.'

'Normalizing shrinks the arrow to a standard length of one without turning it, so multiplying by speed gives you exactly that speed, whatever the distance.'

47. Now break it, on purpose

Concept

Seven experiments, each under a minute.

changewhat happens
Tick Reverse Subtractionthe seeker flees - smoothly, with no error
Delete .normalizedit starts fast and slows as it arrives
Delete Time.deltaTimethe speed depends on the frame rate
Move the target to (3, 5, 4)the seeker flies - zero the Y before normalizing for ground movement
Ask for Vector3.up expecting (0, 0, 1)it is (0, 1, 0). Unity is Y-up
Compare Distance < 5 with sqrMagnitude < 25same answer, one square root cheaper
Normalize a zero vectoryou get (0, 0, 0) and no error - guard before using it for rotation

48. One symptom, four suspects

Elimination

An enemy chases the player correctly but sinks into the floor as it goes.

Eliminate the wrong options

What is the most likely cause?

  • A. The offset includes a Y component, because the player's pivot is at their feet and the enemy's is at its centre.
  • B. The direction was not normalized.
  • C. The subtraction order is reversed.
  • D. Time.deltaTime is missing.

Survives elimination: A

Why: The direction is fully 3D, so any height difference between the two pivots becomes a downward component that the enemy dutifully follows. The standard fix is to flatten before normalizing: take the offset, set its y to 0, then normalize - which is worth knowing because almost every ground-based chase needs it.

49. The procedure: move anything towards anything

Pattern

Four lines, in this order, every time.

  1. Offset: Vector3 toTarget = target.position - transform.position; - destination minus start, and name it so the order is obvious.
  2. Distance, if you need it: toTarget.magnitude, or sqrMagnitude against a squared threshold when it runs every frame.
  3. Direction: toTarget.normalized - length 1, same way. Flatten the Y first if the movement is on the ground.
  4. Move: += direction * speed * Time.deltaTime - or Vector3.MoveTowards, which does the clamping for you.

And when you need to know whether to move: Vector3.Dot(transform.forward, direction) for in-front, Vector3.Cross(...).y for left-or-right.

50. Check 1: the direction

Check

Check your understanding

A seeker at (0, 0, 0) wants the direction to a target at (3, 0, 4). Which expression gives a unit-length vector pointing at the target?

  • A. (transform.position - target.position).normalized
  • B. (target.position - transform.position).normalized (correct)
  • C. target.position.normalized
  • D. (target.position - transform.position).magnitude

Answer: B

Why: Destination minus start gives (3, 0, 4), and normalizing gives (0.6, 0, 0.8) - length 1, pointing at the target. Both halves matter: the order decides the direction, the normalize decides the length.

Why A tempts people
The operands are swapped, so this points away from the target. It is a perfectly good flee vector and a completely wrong chase vector.
Why C tempts people
That normalizes the target's position vector - the direction from the world ORIGIN to the target, which only coincides with the answer when the seeker happens to be at the origin.
Why D tempts people
magnitude is a float - a distance, not a direction. It cannot be added to a position at all.

51. Check 2: what normalize does

Check

Check your understanding

What does .normalized change about a vector?

  • A. Its direction, rotating it onto the nearest axis
  • B. Its length, setting it to 1 and leaving the direction unchanged (correct)
  • C. Both its length and direction
  • D. Nothing - it returns the same vector

Answer: B

Why: Every component is divided by the same number, so their ratios - which are the direction - stay exactly as they were, and the length becomes 1. Vector3.Angle between a vector and its normalized form is always 0.

Why A tempts people
Nothing about normalizing snaps to an axis. (3, 0, 4) becomes (0.6, 0, 0.8), which is not an axis direction at all.
Why C tempts people
The direction is exactly what normalizing preserves - it is the whole point of having the operation.
Why D tempts people
It returns a different vector unless the original already had length 1, and the length is usually the thing you needed to change.

52. One question to sit with

Socratic

Discussion prompt

Why does the dot product of two normalized vectors happen to equal the cosine of the angle between them?

Answer:

Because the dot product measures how much of one arrow lies along the other - its shadow - and for arrows of length 1 that shadow is exactly the cosine by definition of cosine as adjacent over hypotenuse, with the hypotenuse being 1.

That is also why forgetting to normalize breaks the interpretation but not the sign: the shadow gets scaled by the lengths, so 'in front or behind' survives while 'what angle' does not.

53. Check 3: which axis

Check

Check your understanding

You want to move an object straight up. Which is correct in Unity?

  • A. transform.position += Vector3.forward;
  • B. transform.position += Vector3.up; (correct)
  • C. transform.position += new Vector3(0, 0, 1);
  • D. transform.position += Vector3.right;

Answer: B

Why: Vector3.up is (0, 1, 0), and Y is the vertical axis in Unity. Height is Y, depth is Z, sideways is X.

Why A tempts people
Vector3.forward is (0, 0, 1) - deeper into the scene. From the default camera angle that barely looks like movement at all, which is why the mistake survives.
Why C tempts people
Same as A written out longhand: this is the Z-up habit from Blender and most CAD tools, and it is the misconception the diagnostic recorded.
Why D tempts people
Vector3.right is (1, 0, 0) - sideways.

54. A quick number

Estimation

An object moves at 4 m/s towards a target 10 metres away, in a straight line.

Predict first

Roughly how long until it arrives?

  • 0.4 seconds
  • 2.5 seconds
  • 4 seconds
  • 40 seconds

Correct: 2.5 seconds.

Why: 10 divided by 4. It is only that simple because the direction was normalized - the speed is genuinely constant. Without normalizing, the object would start at 40 m/s and slow continuously, and no arrival time could be worked out in your head at all.

55. What else do you need to know?

Missing information

Discussion prompt

'Make the enemy face the player.' What do you need to establish before writing it?

Answer:

  • Face with its whole body, or only turn on the ground plane? A full LookRotation will tilt the enemy up or down if the player is above or below.
  • Instantly, or turn at a speed? Instant is transform.rotation = Quaternion.LookRotation(dir); a turn rate needs Quaternion.RotateTowards or Slerp.
  • What is its forward? If the model was authored facing -Z, everything will be backwards and no vector maths will fix it - the model needs an empty parent to correct it.
  • What if they are in exactly the same place? LookRotation on a zero vector complains; guard it.

The third one catches people for hours, and it is not a maths problem at all - it is an asset problem, which is worth recognising quickly.

56. What this unlocks

Concept

Direction vectors are the language of gameplay code. Lesson 8's callbacks tell you that something touched you; vectors tell you where it came from and where to send it.

you now havewhat it enables
destination minus startchasing, aiming, fleeing, knockback
.normalizedconstant speed, independent of distance
magnitude / sqrMagnituderange checks that are correct and cheap
Dot and Crossfield of view, facing, left-or-right

57. Connect it to something you know

Analogy

Match the pairs

Match each idea to its everyday cousin.

  • u1. target - me
  • u2. .normalized
  • u3. .magnitude
  • u4. Vector3.Dot
  • o1. the route from here to there
  • o2. a compass bearing with the distance stripped off
  • o3. the odometer reading
  • o4. how much of your journey was in the direction you meant

Why: The bearing analogy is the one to keep. A bearing tells you which way to walk and says nothing about how far - which is exactly why you multiply it by a speed, and why the vector you multiply must have had its distance stripped out first.

58. Draw the four-line pattern

Connect it up

Draw it

Draw two dots labelled me and target. Draw the offset arrow with its length written on it, then the normalized arrow starting from the same point with 'length 1' on it, then write the four lines of the move-towards pattern beside them.

59. Before you close this

Exit ticket

Predict first

Which is still shakiest?

  • Which axis is up
  • The subtraction order
  • What normalizing does
  • The dot product

Correct: Whichever you picked - open the lab and read that PASS line for yourself.

Why: This was 1 out of 3 in the diagnostic with the correct answer marked a guess, so expect one of these to still feel unsteady. The lab asserts the first three by name every run, and the watcher prints the fourth once a second.

60. What you can now do

Recap

You can compute a direction, check it on paper, and move something along it at a speed you chose.

Lab: unity-labs/Assets/NaruhodoLabs/Lesson07_Vectors/SETUP.md. Check the 3-4-5 numbers by hand, then go to lesson 8.

Sources

  1. Unity Scripting API - Vector3
  2. Unity Scripting API - Vector3.normalized
  3. Unity Scripting API - Vector3.Dot
  4. Unity Scripting API - Vector3.Distance
  5. Unity Manual - Positioning GameObjects (coordinate system)
  6. Naruhodo Unity Labs - Lesson 07 setup notes — unity-labs/Assets/NaruhodoLabs/Lesson07_Vectors/SETUP.md

Want this taught 1-on-1? Alexander tutors Unity Game Engine — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108