Skip to content

vec

A point in space, or a direction. vec is the maths helper every position, rotation and direction in this API is made of.

luau
local vec = require("vec")

Making one

luau
vec()                       -- (0, 0, 0)
vec(5)                      -- (5, 5, 5) — the same on every axis
vec(1, 2, 3)                -- per axis
vec({ x = 1, y = 2, z = 3 }) -- a copy of any table with x, y, z
vec(self:position())        -- a copy of another vec

You rarely need to build one. Every reading in this API already hands you a vec — self:position(), self:rotation(), hit.pos, hit.normal, shot.eye, shot.dir, player.pos, player.look. And every verb that takes a point also accepts a plain { x =, y =, z = } table. vec is what you reach for when you want to do maths on one.

Maths on points

luau
local a = vec(1, 0, 0)
local b = vec(0, 2, 0)

a + b            -- vec(1, 2, 0)
a - b            -- vec(1, -2, 0)
-a               -- vec(-1, 0, 0)
a * 3            -- vec(3, 0, 0)   — scaling
3 * a            -- the same
a / 2            -- vec(0.5, 0, 0)
a == b           -- false
tostring(a)      -- "vec(1, 0, 0)"

Two you will use constantly:

luau
-- a step of 2 metres in the direction somebody is looking
local ahead = player.pos + player.look * 2

-- just off a surface, so a decal does not flicker against it
local safe = hit.pos + hit.normal * 0.006

Methods

len

v:len() → number

How long the vector is. On a direction it is 1; on the difference between two points it is the distance between them.

dist

v:dist(other) → number

Distance between two points. Clearer than (a - b):len() and does the same thing.

luau
if self:position():dist(player.pos) > 20 then
    return      -- too far away to care
end

normalize

v:normalize() → vec

The same direction, one metre long. This is what turns "the way from me to you" into something you can multiply by a speed.

luau
local away = (target - here):normalize()
self:set_velocity(away * 12)

A zero-length vector normalizes to zero rather than to an error or a broken number, so you never have to guard against it.

dot

v:dot(other) → number

How much two directions agree. 1 is the same way, 0 is a right angle, -1 is opposite.

The practical use is a field of view — "is this in front of me?":

luau
local to_player = (player.pos - here):normalize()
if facing:dot(to_player) > 0.7 then
    -- roughly within a 45° cone in front
end

cross

v:cross(other) → vec

A direction at right angles to both. Mostly used to find "sideways" from "forward" and "up".

luau
local right = forward:cross(vec(0, 1, 0)):normalize()

lerp

v:lerp(other, t) → vec

A point part-way between two. t = 0 is where you started, t = 1 is the target, 0.5 is halfway.

luau
-- creep a tenth of the way toward the target each tick
self:move_to(here:lerp(target, 0.1))

unpack

v:unpack() → three numbers

luau
local x, y, z = here:unpack()

Notes

A vec never changes. Every operation returns a new one, so handing yours to something else can never come back modified.

It costs nothing. vec is pure arithmetic inside your script — it never touches the world, never counts against your effect budget, and never needs a permission. Misusing it (multiplying two vecs, say) is an ordinary script error, like any other bug.

See also

Hungrit scripting documentation.