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) | number | XY 平面模 |
v:length_sq() | number | 模平方(避免开方) |
v:dot(other) | number | 点积 |
v:cross(other) (Vec3 only) | Vec3 | 叉积 |
v:dist(other) | number | 距离 |
v:dist_2d(other) (Vec3 only) | number | XY 平面距离 |
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=false 时 sx/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) | t² 慢起快收 | 渐入 / 从静止启动 |
math.ease_out(t) | 1-(1-t)² 快起慢收 | 减速到停 / 弹出 toast |
math.ease_in_out(t) | cosine 双向 | 通用平滑 |
math.ease_in_cubic(t) | 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)