OptionalonOptional change hook invoked at the end of every mutator. Engine-internal: owners install it, user code normally leaves it unset. It is intentionally not copied by Vector3.copy or Vector3.clone.
Allocates a new vector with the same components. The clone has no change hook. This is the only allocating method on the type (§7b).
Copies the components of v into this vector. The change hook is not copied.
Right-handed cross product, mutating this vector to this × v (this
vector is the left operand). Reading the result requires the temporaries
below because two components are overwritten before the third is read.
Aliasing-safe: v may be this. Both operands are read into scalars
first, not just the receiver — reading v live while writing this is
the whole bug when the two are the same object, and a.cross(a) must be
the zero vector.
Component-wise approximate equality: true when every component differs by
at most epsilon (absolute tolerance).
Euclidean length.
Squared length. Preferred over Vector3.length for comparisons.
Moves this vector toward target by the factor t, mutating this vector.
t = 0 leaves it unchanged, t = 1 makes it equal to target. Values
outside [0, 1] extrapolate; no clamping is applied.
Scales this vector to unit length.
Zero-length behaviour (defined here): normalizing a zero-length vector
leaves it at (0, 0, 0) rather than producing NaN or throwing — there is
no meaningful direction to pick, and poisoning a hot path with NaN is
worse than a no-op. The change hook still fires.
Range behaviour — the guard below tests the squared length, so three ranges leave this vector non-unit and none of them reports an error (measured from a consumer seat, dogfood cycle 10):
0 — every
component below about 1.57e-162 — takes the zero-length branch and is
left unchanged, non-unit. Just above that bound the square is denormal
and has lost most of its mantissa, so the result can be as far off as
0.707 in length.1.3407807929942597e+154 squares
to Infinity, so 1 / Math.sqrt(lengthSquared) is 0 and every
component is written as 0: a large finite vector normalizes to the
zero vector.NaN in that slot and
0 in the others. A NaN component leaves the vector untouched,
because NaN > 0 is false. The zero-length promise above covers a zero
input, not a non-finite one — normalize() does produce NaN here.Nothing clamps and nothing throws (§85, §61): a hot path that can see such magnitudes scales into range before normalizing.
Multiplies every component by the scalar s.
Sets all three components.
Mutable three-component vector (§7b) — the primary vector type of the engine (§7 transforms, §11 motion, §20+ physics). The world is right-handed with +Y up in both 2D and 3D (§7a).
Allocation policy (§7b, plan D7): instance methods that produce a "this-shaped" result mutate in place and return
this; only Vector3.clone allocates. Scalar queries (dot,lengthSq,length) andequalsApproxnever allocate and never mutate.Change notification (plan D3): every mutator invokes Vector3.onChanged after writing. Direct field writes (
v.x = 1) bypass the hook by design.