【Laya】MathUtil 数学工具类使用说明
Laya.MathUtil是LayaAir引擎提供的静态数学工具类,包含常用数值计算方法。主要功能包括:线性插值(lerp)实现平滑过渡;数值循环(repeat)处理循环动画;距离计算(distance)测量两点间距;数值限制(clamp)确保值在指定范围内;以及四元数插值、角度计算、数组排序等功能。该类提供简单易用的API,适用于游戏开发中的各种数值处理需求,如平滑移动、颜色渐变、循环动画、碰撞
Laya.MathUtil 数学工具类使用说明
简介
Laya.MathUtil 是 LayaAir 引擎提供的静态数学工具类,包含常用的数值计算方法,涵盖线性插值、数值限制、距离计算、角度计算、数组排序等功能。
目录
线性插值
lerp
在两个值之间进行线性插值。
static lerp(left: number, right: number, amount: number): number
参数:
left: 起始值right: 终止值amount: 插值比率(0-1)
返回值: 插值结果
示例:
// 基本用法
let result1 = Laya.MathUtil.lerp(0, 100, 0.5); // 50
let result2 = Laya.MathUtil.lerp(0, 100, 0.25); // 25
let result3 = Laya.MathUtil.lerp(0, 100, 0.75); // 75
let result4 = Laya.MathUtil.lerp(50, 150, 0.5); // 100
// 实际应用:平滑移动动画
class SmoothMovement {
private startX: number = 0;
private endX: number = 500;
private progress: number = 0;
private sprite: Laya.Sprite;
update(deltaTime: number): void {
this.progress += deltaTime * 0.001; // 假设 delta 单位是毫秒
if (this.progress > 1) this.progress = 1;
// 使用 lerp 计算当前位置
this.sprite.x = Laya.MathUtil.lerp(
this.startX,
this.endX,
this.progress
);
}
}
// 实际应用:颜色渐变
function lerpColor(color1: number, color2: number, t: number): number {
let r1 = (color1 >> 16) & 0xFF;
let g1 = (color1 >> 8) & 0xFF;
let b1 = color1 & 0xFF;
let r2 = (color2 >> 16) & 0xFF;
let g2 = (color2 >> 8) & 0xFF;
let b2 = color2 & 0xFF;
let r = Math.floor(Laya.MathUtil.lerp(r1, r2, t));
let g = Math.floor(Laya.MathUtil.lerp(g1, g2, t));
let b = Math.floor(Laya.MathUtil.lerp(b1, b2, t));
return (r << 16) | (g << 8) | b;
}
// 从红色渐变到蓝色
let color = lerpColor(0xFF0000, 0x0000FF, 0.5); // 0x800080 (紫色)
数值循环
repeat
将值 t 重复限定在 [0, length) 范围内。
static repeat(t: number, length: number): number
参数:
t: 要重复的值length: 范围长度
返回值: 重复后的值,范围在 [0, length) 之间
示例:
// 基本用法
let result1 = Laya.MathUtil.repeat(5, 10); // 5
let result2 = Laya.MathUtil.repeat(15, 10); // 5 (15 % 10 = 5)
let result3 = Laya.MathUtil.repeat(-3, 10); // 7
let result4 = Laya.MathUtil.repeat(0, 10); // 0
// 实际应用:循环动画
class LoopingAnimation {
private time: number = 0;
private loopDuration: number = 2000; // 2秒循环
update(deltaTime: number): number {
this.time += deltaTime;
// 获取循环内的进度 [0, 1)
let progress = Laya.MathUtil.repeat(this.time, this.loopDuration) / this.loopDuration;
return progress;
}
}
// 实际应用:无限滚动的背景
class ScrollingBackground {
private scrollX: number = 0;
private textureWidth: number = 800;
scroll(speed: number): void {
this.scrollX += speed;
// 保持在纹理宽度范围内
this.scrollX = Laya.MathUtil.repeat(this.scrollX, this.textureWidth);
}
}
距离计算
distance
计算二维空间中两点之间的距离。
static distance(x1: number, y1: number, x2: number, y2: number): number
参数:
x1: 第一个点的 X 坐标y1: 第一个点的 Y 坐标x2: 第二个点的 X 坐标y2: 第二个点的 Y 坐标
返回值: 两点之间的距离
示例:
// 基本用法
let dist1 = Laya.MathUtil.distance(0, 0, 100, 0); // 100
let dist2 = Laya.MathUtil.distance(0, 0, 0, 100); // 100
let dist3 = Laya.MathUtil.distance(0, 0, 100, 100); // ≈ 141.42
let dist4 = Laya.MathUtil.distance(50, 50, 100, 100); // ≈ 70.71
// 实际应用:检测敌人是否进入攻击范围
class EnemyDetection {
private playerX: number = 400;
private playerY: number = 300;
private attackRange: number = 150;
isInRange(enemyX: number, enemyY: number): boolean {
let dist = Laya.MathUtil.distance(
this.playerX,
this.playerY,
enemyX,
enemyY
);
return dist <= this.attackRange;
}
}
// 实际应用:寻找最近的目标
class TargetSelector {
findNearest(x: number, y: number, targets: Array<{x: number, y: number}>): {x: number, y: number} | null {
let nearest: {x: number, y: number} | null = null;
let minDistance = Infinity;
for (let target of targets) {
let dist = Laya.MathUtil.distance(x, y, target.x, target.y);
if (dist < minDistance) {
minDistance = dist;
nearest = target;
}
}
return nearest;
}
}
数值限制
clamp
将值限制在指定范围内。
static clamp(value: number, min: number, max: number): number
参数:
value: 要限制的值min: 最小值max: 最大值
返回值: 限制后的值
示例:
// 基本用法
let result1 = Laya.MathUtil.clamp(50, 0, 100); // 50
let result2 = Laya.MathUtil.clamp(-10, 0, 100); // 0
let result3 = Laya.MathUtil.clamp(150, 0, 100); // 100
let result4 = Laya.MathUtil.clamp(0, -50, 50); // 0
// 实际应用:限制血量范围
class Character {
private maxHealth: number = 100;
private currentHealth: number = 100;
takeDamage(damage: number): void {
this.currentHealth = Laya.MathUtil.clamp(
this.currentHealth - damage,
0,
this.maxHealth
);
}
heal(amount: number): void {
this.currentHealth = Laya.MathUtil.clamp(
this.currentHealth + amount,
0,
this.maxHealth
);
}
}
// 实际应用:限制速度范围
class MovableObject {
private speed: number = 0;
private minSpeed: number = -200;
private maxSpeed: number = 200;
setSpeed(newSpeed: number): void {
this.speed = Laya.MathUtil.clamp(newSpeed, this.minSpeed, this.maxSpeed);
}
}
clamp01
将值限制在 [0, 1] 范围内。
static clamp01(value: number): number
参数:
value: 要限制的值
返回值: 限制后的值
示例:
// 基本用法
let result1 = Laya.MathUtil.clamp01(0.5); // 0.5
let result2 = Laya.MathUtil.clamp01(-0.5); // 0
let result3 = Laya.MathUtil.clamp01(1.5); // 1
let result4 = Laya.MathUtil.clamp01(0); // 0
// 实际应用:标准化进度值
class ProgressBar {
private currentValue: number = 0;
private maxValue: number = 100;
getProgress(): number {
// 确保进度始终在 0-1 之间
return Laya.MathUtil.clamp01(this.currentValue / this.maxValue);
}
setValue(value: number): void {
this.currentValue = value;
}
}
// 实际应用:透明度限制
class FadeEffect {
private alpha: number = 1;
setAlpha(value: number): void {
// 确保透明度在 0-1 之间
this.alpha = Laya.MathUtil.clamp01(value);
}
}
四元数插值
slerpQuaternionArray
在两个四元数之间进行球面线性插值(slerp)。
static slerpQuaternionArray(
a: Float32Array,
Offset1: number,
b: Float32Array,
Offset2: number,
t: number,
out: Float32Array,
Offset3: number
): Float32Array
参数:
a: 起始四元数数组Offset1: 起始四元数数组的偏移量b: 终止四元数数组Offset2: 终止四元数数组的偏移量t: 插值比率(0-1)out: 用于存储结果的输出四元数数组Offset3: 输出四元数数组的偏移量
返回值: 输出四元数数组
示例:
// 实际应用:3D 对象旋转平滑过渡
class SmoothRotation3D {
private startRotation = new Float32Array([0, 0, 0, 1]);
private endRotation = new Float32Array([0, 0.707, 0, 0.707]);
private currentRotation = new Float32Array([0, 0, 0, 1]);
private progress: number = 0;
update(deltaTime: number): void {
this.progress += deltaTime * 0.001;
if (this.progress > 1) this.progress = 1;
Laya.MathUtil.slerpQuaternionArray(
this.startRotation, 0,
this.endRotation, 0,
this.progress,
this.currentRotation, 0
);
}
}
角度计算
getRotation
获取由两个指定点组成的线段的角度值。
static getRotation(x0: number, y0: number, x1: number, y1: number): number
参数:
x0: 第一个点的 X 坐标y0: 第一个点的 Y 坐标x1: 第二个点的 X 坐标y1: 第二个点的 Y 坐标
返回值: 角度值,单位为度
示例:
// 基本用法
let angle1 = Laya.MathUtil.getRotation(0, 0, 100, 0); // 0° (向右)
let angle2 = Laya.MathUtil.getRotation(0, 0, 0, 100); // 90° (向下)
let angle3 = Laya.MathUtil.getRotation(0, 0, -100, 0); // 180° (向左)
let angle4 = Laya.MathUtil.getRotation(0, 0, 0, -100); // -90° (向上)
let angle5 = Laya.MathUtil.getRotation(0, 0, 100, 100); // 45° (右下)
// 实际应用:让炮台朝向目标
class Turret {
private turretSprite: Laya.Sprite;
private turretX: number = 400;
private turretY: number = 300;
aimAt(targetX: number, targetY: number): void {
// 计算朝向目标的角度
let angle = Laya.MathUtil.getRotation(
this.turretX,
this.turretY,
targetX,
targetY
);
this.turretSprite.rotation = angle;
}
}
// 实际应用:子弹发射方向
class Bullet {
static create(
startX: number,
startY: number,
targetX: number,
targetY: number,
speed: number
): { x: number; y: number } {
let angle = Laya.MathUtil.getRotation(startX, startY, targetX, targetY);
let radians = angle * Math.PI / 180;
return {
x: Math.cos(radians) * speed,
y: Math.sin(radians) * speed
};
}
}
// 实际应用:让角色朝向移动方向
class Character {
private sprite: Laya.Sprite;
private currentX: number = 0;
private currentY: number = 0;
moveTo(targetX: number, targetY: number): void {
// 计算移动方向的角度
let angle = Laya.MathUtil.getRotation(
this.currentX,
this.currentY,
targetX,
targetY
);
this.sprite.rotation = angle;
// 更新位置...
}
}
数组排序
sortBigFirst
数值从大到小排序的比较函数。
static sortBigFirst(a: number, b: number): number
参数:
a: 待比较数字b: 待比较数字
返回值: 如果 a 等于 b 返回 0;如果 b > a 返回 1;如果 b < a 返回 -1
示例:
// 基本用法
let scores = [85, 92, 78, 95, 88];
scores.sort(Laya.MathUtil.sortBigFirst);
console.log(scores); // [95, 92, 88, 85, 78]
// 实际应用:排行榜按分数排序
class Leaderboard {
private entries: Array<{name: string, score: number}> = [];
addEntry(name: string, score: number): void {
this.entries.push({ name, score });
this.sortByScore();
}
sortByScore(): void {
this.entries.sort((a, b) => Laya.MathUtil.sortBigFirst(a.score, b.score));
}
getTopPlayers(count: number): Array<{name: string, score: number}> {
return this.entries.slice(0, count);
}
}
sortSmallFirst
数值从小到大排序的比较函数。
static sortSmallFirst(a: number, b: number): number
参数:
a: 待比较数字b: 待比较数字
返回值: 如果 a 等于 b 返回 0;如果 b > a 返回 -1;如果 b < a 返回 1
示例:
// 基本用法
let numbers = [5, 2, 8, 1, 9];
numbers.sort(Laya.MathUtil.sortSmallFirst);
console.log(numbers); // [1, 2, 5, 8, 9]
// 实际应用:按价格排序商品
class Shop {
private items: Array<{name: string, price: number}> = [];
sortByPrice(): void {
this.items.sort((a, b) => Laya.MathUtil.sortSmallFirst(a.price, b.price));
}
}
sortNumBigFirst
将元素转为数字后从大到小排序的比较函数。
static sortNumBigFirst(a: any, b: any): number
参数:
a: 待比较元素b: 待比较元素
返回值: b、a 转化成数字的差值 (b-a)
示例:
// 基本用法:处理字符串数字
let strNumbers = ["95", "88", "92", "78"];
strNumbers.sort(Laya.MathUtil.sortNumBigFirst);
console.log(strNumbers); // ["95", "92", "88", "78"]
// 实际应用:按数字属性排序
class QuestManager {
private quests: Array<{id: string, priority: string}> = [
{ id: "quest1", priority: "3" },
{ id: "quest2", priority: "1" },
{ id: "quest3", priority: "2" }
];
sortByPriority(): void {
this.quests.sort((a, b) =>
Laya.MathUtil.sortNumBigFirst(a.priority, b.priority)
);
}
}
sortNumSmallFirst
将元素转为数字后从小到大排序的比较函数。
static sortNumSmallFirst(a: any, b: any): number
参数:
a: 待比较元素b: 待比较元素
返回值: a、b 转化成数字的差值 (a-b)
示例:
// 基本用法
let values = ["100", "25", "50", "10"];
values.sort(Laya.MathUtil.sortNumSmallFirst);
console.log(values); // ["10", "25", "50", "100"]
sortByKey
根据对象指定的属性进行排序的比较函数。
static sortByKey(key: string, bigFirst?: boolean, forceNum?: boolean): (a: any, b: any) => number
参数:
key: 排序要依据的元素属性名bigFirst: 是否从大到小排序,默认false(从小到大)forceNum: 是否将元素转为数字比较,默认false
返回值: 排序函数
示例:
// 基本用法
interface Player {
name: string;
level: number;
score: string;
}
let players: Player[] = [
{ name: "Alice", level: 10, score: "1500" },
{ name: "Bob", level: 5, score: "2000" },
{ name: "Charlie", level: 15, score: "1000" }
];
// 按等级从高到低排序
players.sort(Laya.MathUtil.sortByKey("level", true));
// 结果: Charlie(15), Alice(10), Bob(5)
// 按分数从低到高排序(转为数字)
players.sort(Laya.MathUtil.sortByKey("score", false, true));
// 结果: Charlie(1000), Alice(1500), Bob(2000)
// 实际应用:动态排序
class SortableList<T> {
private items: T[] = [];
sortBy(property: string, descending: boolean = false): void {
this.items.sort(Laya.MathUtil.sortByKey(property, descending));
}
}
// 实际应用:商品排序
class ProductManager {
private products: Array<{
name: string;
price: number;
sales: string;
rating: number;
}> = [];
// 按价格从低到高
sortByPrice(): void {
this.products.sort(Laya.MathUtil.sortByKey("price", false));
}
// 按销量从高到低(转为数字)
sortBySales(): void {
this.products.sort(Laya.MathUtil.sortByKey("sales", true, true));
}
// 按评分从高到低
sortByRating(): void {
this.products.sort(Laya.MathUtil.sortByKey("rating", true));
}
}
数值精度处理
roundTo
将数字按小数位四舍五入,并将接近 0 的值归零,避免浮点误差。
static roundTo(value: number, decimals?: number, epsilon?: number): number
参数:
value: 输入数字decimals: 保留小数位数,默认 3epsilon: 接近 0 判定阈值,默认 1e-6
返回值: 处理后的数字
示例:
// 基本用法
let result1 = Laya.MathUtil.roundTo(3.1415926); // 3.142
let result2 = Laya.MathUtil.roundTo(3.1415926, 2); // 3.14
let result3 = Laya.MathUtil.roundTo(3.1415926, 4); // 3.1416
// 处理浮点误差
let result4 = Laya.MathUtil.roundTo(0.0000001); // 0 (小于 epsilon)
let result5 = Laya.MathUtil.roundTo(0.000001); // 0 (等于 epsilon)
let result6 = Laya.MathUtil.roundTo(0.00001); // 0.00001 (大于 epsilon)
// 实际应用:显示精度控制
class ScoreDisplay {
private rawScore: number = 0;
getFormattedScore(decimals: number = 1): string {
return Laya.MathUtil.roundTo(this.rawScore, decimals).toFixed(decimals);
}
addScore(points: number): void {
this.rawScore += points;
// 累积后清理浮点误差
this.rawScore = Laya.MathUtil.roundTo(this.rawScore, 3);
}
}
// 实际应用:物理模拟中的精度控制
class PhysicsSimulation {
private position: { x: number; y: number } = { x: 0, y: 0 };
private velocity: { x: number; y: number } = { x: 0, y: 0 };
update(dt: number): void {
// 更新位置
this.position.x += this.velocity.x * dt;
this.position.y += this.velocity.y * dt;
// 清理浮点累积误差
this.position.x = Laya.MathUtil.roundTo(this.position.x, 4);
this.position.y = Laya.MathUtil.roundTo(this.position.y, 4);
}
}
// 实际应用:货币计算
class Wallet {
private balance: number = 0;
add(amount: number): void {
this.balance += amount;
this.balance = Laya.MathUtil.roundTo(this.balance, 2);
}
getBalance(): number {
return Laya.MathUtil.roundTo(this.balance, 2);
}
}
完整示例
以下是一个综合使用 Laya.MathUtil 工具类的完整示例:
export async function main() {
await Laya.init(800, 600);
// 创建游戏场景
let scene = new Laya.Scene();
Laya.stage.addChild(scene);
// 1. 创建玩家精灵
let player = new Laya.Sprite();
player.graphics.drawCircle(0, 0, 20, "#00FF00");
player.pos(400, 300);
Laya.stage.addChild(player);
// 2. 创建目标点
let target = new Laya.Sprite();
target.graphics.drawCircle(0, 0, 10, "#FF0000");
target.pos(600, 400);
Laya.stage.addChild(target);
// 3. 使用 getRotation 让玩家朝向目标
let angle = Laya.MathUtil.getRotation(
player.x,
player.y,
target.x,
target.y
);
console.log("朝向角度:", angle);
// 4. 使用 distance 计算距离
let distance = Laya.MathUtil.distance(
player.x,
player.y,
target.x,
target.y
);
console.log("目标距离:", distance);
// 5. 使用 lerp 实现平滑移动
let progress = 0;
let speed = 0.02;
Laya.timer.frameLoop(1, this, () => {
if (progress < 1) {
progress = Laya.MathUtil.clamp01(progress + speed);
player.x = Laya.MathUtil.lerp(400, 600, progress);
player.y = Laya.MathUtil.lerp(300, 400, progress);
}
});
// 6. 使用 sortByKey 对玩家分数排序
interface PlayerData {
name: string;
score: number;
}
let leaderboard: PlayerData[] = [
{ name: "玩家A", score: 1500 },
{ name: "玩家B", score: 2000 },
{ name: "玩家C", score: 1200 }
];
// 按分数从高到低排序
leaderboard.sort(Laya.MathUtil.sortByKey("score", true));
console.log("排行榜:", leaderboard);
// 7. 使用 roundTo 处理浮点数
let piRounded = Laya.MathUtil.roundTo(Math.PI, 2);
console.log("PI (保留2位小数):", piRounded);
// 8. 使用 clamp 限制血量范围
class HealthSystem {
private health: number = 80;
private maxHealth: number = 100;
takeDamage(damage: number): void {
this.health = Laya.MathUtil.clamp(
this.health - damage,
0,
this.maxHealth
);
console.log("当前血量:", this.health);
}
heal(amount: number): void {
this.health = Laya.MathUtil.clamp(
this.health + amount,
0,
this.maxHealth
);
console.log("当前血量:", this.health);
}
}
let healthSystem = new HealthSystem();
healthSystem.takeDamage(30); // 50
healthSystem.heal(70); // 100 (不会超过最大值)
// 9. 使用 repeat 实现循环动画
let animTime = 0;
Laya.timer.frameLoop(1, this, () => {
animTime += 16; // 假设每帧约 16ms
// 让进度在 0-2 秒内循环
let loopProgress = Laya.MathUtil.repeat(animTime, 2000) / 2000;
console.log("循环进度:", loopProgress);
});
// 10. 使用 sortBigFirst 排序数组
let scores = [85, 92, 78, 95, 88];
scores.sort(Laya.MathUtil.sortBigFirst);
console.log("分数排序:", scores); // [95, 92, 88, 85, 78]
}
注意事项
-
静态方法:
MathUtil类的所有方法都是静态的,使用时必须加Laya.前缀。 -
角度 vs 弧度:
getRotation()返回的是度数(degrees),可直接用于Sprite.rotation- 使用
Math.sin()、Math.cos()等函数时需先转换为弧度 - 可使用
Laya.Utils.toRadian()进行转换
-
浮点精度: 由于 JavaScript 浮点数计算可能产生精度误差,建议使用
roundTo()方法进行精度控制。 -
数组排序:
sortBigFirst/sortSmallFirst只能排序数字数组,如需排序对象数组请使用sortByKey()。 -
clamp 参数顺序:
clamp(value, min, max)的参数顺序是 value、min、max,注意不要写成 min、max、value。 -
版本信息: 本文档基于 LayaAir 3.3.6 版本编写。
更多推荐


所有评论(0)