Hungrit Docs

vec#

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

Use it to

  • add, subtract and scale positions
  • measure a distance, or the direction from one thing to another
  • find a point part-way between two
  • turn, bounce or limit a direction

At a glance#

What you want done, and the verb that does it.

Luau
local vec = require("vec")

Making one#

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

Tip

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)   scaling3 * a            -- the samea / 2            -- vec(0.5, 0, 0)a == b           -- falsetostring(a)      -- "vec(1, 0, 0)"

Two you will use constantly:

Luau
-- a step of 2 metres in the direction somebody is lookinglocal ahead = player.pos + player.look * 2 -- just off a surface, so a decal does not flicker against itlocal 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 careend

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, meaning "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 frontend

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 tickself:move_to(here:lerp(target, 0.1))

unpack#

v:unpack() → three numbers

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

angle#

v:angle(other) → number

The angle between two directions, in degrees, from 0 (same way) to 180 (opposite). A zero vector has no direction and answers 0.

Luau
if facing:angle(player.pos - here) < 30 then    -- within a 60° cone in frontend

reflect#

v:reflect(normal) → vec

The direction after bouncing off a surface. normal is the surface's normal, which is exactly what a raycast hands you.

Luau
local hit = world:raycast({ from = here, dir = dir, max = 50 })if hit then    dir = dir:reflect(hit.normal)   -- the ricochetend

project#

v:project(onto) → vec

The part of v that points along onto. Take it away and what is left runs across it, which is how you slide along a wall instead of stopping dead:

Luau
local along_wall = velocity - velocity:project(hit.normal)

rotate_y#

v:rotate_y(degrees) → vec

Turned about the vertical axis, the same turn self:rotate_by(vec(0, degrees, 0)) gives an object. Handy for spreading shots or placing things in a ring:

Luau
for i = 0, 7 do    local spot = here + vec(4, 0, 0):rotate_y(i * 45)    world:spawn({ shape = "sphere", pos = spot, scale = 0.3 })end

clamp_len#

v:clamp_len(max) → vec

The same direction, never longer than max. A speed limit:

Luau
self:set_velocity((self:velocity() + push):clamp_len(15))

flat#

v:flat() → vec

The same vector with y set to zero: its horizontal part. Use it before normalize when something should turn toward a player without tilting up or down.

Luau
local toward = (player.pos - here):flat():normalize()

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#

Updated 17 October 2026