HOOZi文档
Skip to content

math

扩展 Lua stdlib(不覆盖 sin/cos/pi/floor/random/...),增量挂在 math 表上。


向量类型

math.Vec2(x, y) → Vec2

math.Vec3(x, y, z) → Vec3

注意:是 CamelCase Vec2/Vec3,不是 vec2/vec3(小写 vec2 是另一个内部类型)。

Vec2 / Vec3 字段与运算

字段:.x / .y / .z(Vec3 only)

支持的元运算符(双向都行,左右操作数顺序无关):

运算行为
a + b逐分量加
a - b逐分量减
v * scalar标量乘
v / scalar标量除
-v取反
tostring(v)文本形式,如 Vec3(1.000, 2.000, 3.000)

实例方法:

方法返回说明
v:length()number
v:length_2d() (Vec3 only)numberXY 平面模
v:length_sq()number模平方(避免开方)
v:dot(other)number点积
v:cross(other) (Vec3 only)Vec3叉积
v:dist(other)number距离
v:dist_2d(other) (Vec3 only)numberXY 平面距离
v:to_2d() (Vec3 only)Vec2投影到 Vec2 (x, y)
v:normalize()new Vec原地单位化,但 sol2 按值返回更新后的副本(不是 self 引用)。链式 v:normalize():length() 拿到的不是原 v
v:normalized()new Vec拷贝再单位化,原 v 不变

标量运算

math.Lerp(a, b, t) → number

线性插值。t 不 clamp,外推合法(用户负责)。

math.Clamp(x, lo, hi) → number

math.Smoothstep(edge0, edge1, x) → number

Hermite 插值,输入 x[edge0, edge1] 内会光滑过渡到 [0, 1],两端 saturate。

math.RemapVal(val, a, b, c, d) → number

线性映射:把 val[a, b] 范围线性映射到 [c, d]不 clamp(超出 [a,b] 会外推)。

math.RemapValClamped(val, a, b, c, d) → number

RemapVal,但输入 val[a, b] saturate(超出范围按端点处理)。

lua
-- 血量 0..100 映射到颜色饱和度 0..1
local sat = math.RemapValClamped(hp, 0, 100, 0.2, 1.0)

math.AngleNormalize(angle_deg) → number

把任意度数规范化到 [-180, 180)


角度 / 投影

math.CalcAngle(from_pos, to_pos) → pitch, yaw

两个 Vec3 → (pitch, yaw) 度数,pitch 向上为负。

math.WorldToScreen(world_pos) → sx, sy, ok

3 返值;ok=falsesx/sy 无意义(out-of-game / 屏幕外 / 视图矩阵未就绪)。


大地图坐标

内置标定表覆盖所有官方地图(Storm Point / World's Edge / Kings Canyon / Olympus / Broken Moon ...)。脚本不必自己维护比例尺。

math.WorldToMapPixel(wx, wy, disp_w, disp_h) → px, py, ok

世界坐标 (wx, wy) → 大地图 PNG 在显示画布 (disp_w, disp_h) 上的像素位置。

  • ok = false:大厅 / 未知 map / 不在游戏内 → (px, py) 兜底为画布中心
  • 跟比例无关只取地图四角标定,disp 缩放跟随画布尺寸

math.WorldRadiusToMapPixel(r_world, disp_size) → r_pixel, ok

世界 inch 半径 → 大地图像素半径。disp_size 通常传画布短边(同方向缩放避免椭圆变形)。

  • 用于死亡圈 / 爆炸范围 / 标记圆等同心圆
  • ok = false 时返 0
lua
event.on("frame_update", function()
    local sw, sh = draw.screen()
    -- 自定义 mini 雷达 canvas:左上 200x200
    for _, p in pairs(game.entities.players) do
        if not p.is_teammate then
            local px, py, ok = math.WorldToMapPixel(p.origin.x, p.origin.y, 200, 200)
            if ok then
                draw.circle(px, py, 3, draw.rgba(1, 0.2, 0.2, 1), { filled = true })
            end
        end
    end

    -- 死亡圈半径
    local rp, ok = math.WorldRadiusToMapPixel(ring_radius_world, 200)
    if ok then
        draw.circle(100, 100, rp, draw.rgba(0.4, 0.7, 1, 0.6), { thickness = 2 })
    end
end)

动画曲线

所有 ease_* 接受 t : number(自动 clamp 到 [0, 1]),返 [0, 1]

函数曲线适合
math.ease_in(t) 慢起快收渐入 / 从静止启动
math.ease_out(t)1-(1-t)² 快起慢收减速到停 / 弹出 toast
math.ease_in_out(t)cosine 双向通用平滑
math.ease_in_cubic(t) 更夸张的慢起高对比加速
math.ease_out_cubic(t)1-(1-t)³ 更夸张的快起慢收高对比减速
math.ease_bounce(t)末尾回弹弹性出现 / 通知抖动

示例

lua
-- 向量运算
local lp = game.localplayer
local p  = game.entities.players  -- 假设有目标
for _, enemy in pairs(p) do
    if not enemy.is_teammate then
        local delta = enemy.origin - lp.origin
        print("敌人距离:", delta:length(), "米")
        local angle_deg = math.CalcAngle(lp.camera_origin, enemy.head_pos)
    end
end

-- 屏幕投影
event.on("frame_update", function(e)
    for _, p in pairs(game.entities.players) do
        if not p.is_teammate and p.head_pos then
            local sx, sy, ok = math.WorldToScreen(p.head_pos)
            if ok then
                draw.circle(sx, sy, 3, draw.rgba(1, 0, 0, 1), { filled = true })
            end
        end
    end
end)

-- 缓动动画:notify 弹出 0.5s 内从 0% 到 100% 不透明
local start_t = time.now()
event.on("frame_update", function(e)
    local elapsed = time.now() - start_t
    local progress = math.Clamp(elapsed / 0.5, 0, 1)
    local alpha = math.ease_out_cubic(progress)
    -- 用 alpha 画 toast
end)