关于 curves-and-paths
面向 Phaser 4 开发者的路径技能,可用曲线组合路径、采样和绘制路径,并通过 PathFollower 驱动精灵沿路径移动。适用于实现弯曲路线与路径动画。
主要功能
- 01用直线、样条、贝塞尔和椭圆曲线构建路径
- 02按参数、弧长或距离获取路径点
- 03使用 Graphics 绘制路径
- 04通过 PathFollower 沿路径移动精灵
- 05控制跟随时长、偏移和旋转
适用场景
- 01为游戏对象创建弯曲路线
- 02绘制路径以用于可视化或游戏画面
- 03让精灵按时长沿路线移动并旋转
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ phaserjs/ phaser --skill curves-and-paths安装后在 Claude Code 中输入/curves-and-paths确认已加载。
详细介绍
曲线与路径
使用曲线创建路径、获取路径上的点、用 Graphics 绘制路径,以及在 Phaser 4 中使用 PathFollower 让精灵自动沿路径移动。
关键源路径: src/curves/、src/curves/path/、src/gameobjects/pathfollower/、src/gameobjects/components/PathFollower.js
相关技能: ../sprites-and-images/SKILL.md、../graphics-and-shapes/SKILL.md、../tweens/SKILL.md
快速开始
// In a Scene's create() method:
// 1. Create a Path starting at (50, 300)
const path = this.add.path(50, 300);
// 2. Add curves to the path
path.lineTo(200, 100);
path.splineTo([ new Phaser.Math.Vector2(300, 400), new Phaser.Math.Vector2(500, 200) ]);
path.lineTo(700, 300);
// 3. Draw the path using Graphics
const graphics = this.add.graphics();
graphics.lineStyle(2, 0xffffff, 1);
path.draw(graphics, 64);
// 4. Create a PathFollower sprite that moves along the path
const follower = this.add.follower(path, 50, 300, 'ship');
follower.startFollow({
duration: 5000,
rotateToPath: true,
repeat: -1,
yoyo: true
});
核心概念
Path
Phaser.Curves.Path 是一个容器,它将多条曲线组合成一条连续的复合曲线。Path 中的曲线不必首尾相连。只有曲线的顺序会影响沿路径的点计算。
通过工厂方法创建:this.add.path(x, y),其中 x/y 是起点。
关键属性:
curves-- Path 中Phaser.Curves.Curve对象的数组startPoint--Vector2,定义的起始位置autoClose-- 布尔值,如果为 true,getPoints()会在末尾追加第一个点defaultDivisions-- 数字(默认值:12),调用getPoints()时每条曲线的分段数name-- 字符串,默认为空,供开发者使用
曲线
所有曲线类型都继承自 Phaser.Curves.Curve(基类)。每条曲线都支持:
getPoint(t, out)-- 根据曲线参数化,获取位置 t(0-1)处的点getPointAt(u, out)-- 根据弧长(均匀间隔),获取位置 u(0-1)处的点getPoints(divisions, stepRate, out)-- 沿曲线的点数组getSpacedPoints(divisions, stepRate, out)-- 按弧长等距的点数组getDistancePoints(distance)-- 按像素距离间隔的点getLength()-- 总弧长(像素)getBounds(out, accuracy)-- 边界 RectanglegetTangent(t, out)/getTangentAt(u, out)-- 单位切向量getStartPoint(out)/getEndPoint(out)-- 第一个/最后一个点getRandomPoint(out)-- 曲线上的随机点draw(graphics, pointsTotal)-- 将曲线渲染到 Graphics 对象上active-- 布尔值,为 false 时父 Path 会跳过这条曲线
PathFollower
Phaser.GameObjects.PathFollower 是带有 Components.PathFollower mixin 的 Sprite。它使用内部 Tween(一个从 0 到 1 的数字计数器)每帧沿 Path 前进。
通过工厂方法创建:this.add.follower(path, x, y, texture, frame)
PathFollower 组件提供:
path-- 正在跟随的Phaser.Curves.PathpathTween-- 驱动移动的内部 TweenpathOffset--Vector2,添加到路径坐标上的偏移量pathVector--Vector2,路径上的当前位置pathDelta--Vector2,自上一帧以来移动的距离rotateToPath-- 布尔值,自动旋转以面向路径方向pathRotationOffset-- 数字(度),添加到自动旋转上
常见模式
使用链式曲线创建路径
Path 提供了便捷方法,可从上一条曲线的终点开始创建曲线:
const path = this.add.path(100, 500);
path.lineTo(300, 100); // straight line
path.cubicBezierTo(500, 100, 350, 50, 450, 50); // cubic bezier (endX, endY, cp1X, cp1Y, cp2X, cp2Y)
path.quadraticBezierTo(700, 400, 600, 100); // quadratic bezier (endX, endY, cpX, cpY)
path.splineTo([ // spline through points
new Phaser.Math.Vector2(750, 300),
new Phaser.Math.Vector2(600, 500)
]);
path.ellipseTo(50, 80, 0, 270, false, 0); // ellipse arc (xRadius, yRadius, startAngle, endAngle, clockwise, rotation)
path.circleTo(40); // shortcut for ellipseTo with equal radii and 0-360
// Jump to a new position without drawing (creates a gap)
path.moveTo(400, 400);
path.lineTo(500, 400);
// Close the path by connecting end to start
path.closePath();
添加独立曲线对象
const path = new Phaser.Curves.Path(0, 0);
// Add pre-constructed curve objects
const line = new Phaser.Curves.Line(new Phaser.Math.Vector2(0, 0), new Phaser.Math.Vector2(200, 200));
path.add(line);
const spline = new Phaser.Curves.Spline([ 200, 200, 300, 100, 400, 300 ]);
path.add(spline);
const ellipse = new Phaser.Curves.Ellipse(400, 300, 100, 60, 0, 360, false, 0);
path.add(ellipse);
获取沿路径的点
// Array of points (uses defaultDivisions per curve)
const points = path.getPoints();
// With explicit divisions per curve
const detailed = path.getPoints(32);
// Equally spaced points along the entire path
const spaced = path.getSpacedPoints(100);
// Single point at normalized position (0-1)
const midpoint = path.getPoint(0.5);
// Tangent vector at a position
const tangent = path.getTangent(0.5);
// Total path length in pixels
const length = path.getLength();
// Bounding rectangle
const bounds = path.getBounds();
使用 Graphics 绘制路径
const graphics = this.add.graphics();
// Draw entire path
graphics.lineStyle(2, 0x00ff00, 1);
path.draw(graphics, 64); // 64 = points per curve for smoothness
// Draw individual curves
graphics.lineStyle(1, 0xff0000, 1);
path.curves[0].draw(graphics, 32);
// Draw debug points
const points = path.getSpacedPoints(50);
points.forEach(p => {
graphics.fillStyle(0xffff00, 1);
graphics.fillCircle(p.x, p.y, 3);
});
PathFollower 精灵
const path = this.add.path(100, 200);
path.lineTo(400, 400);
path.lineTo(700, 200);
// Create follower
const enemy = this.add.follower(path, 100, 200, 'enemy');
// Start following with config
enemy.startFollow({
duration: 3000, // ms to traverse path
positionOnPath: true, // snap to path start position
rotateToPath: true, // auto-rotate to face direction
rotationOffset: 90, // offset added to auto-rotation (degrees)
repeat: -1, // -1 = infinite repeat
yoyo: true, // reverse on each repeat
from: 0, // start position on path (0-1)
to: 1, // end position on path (0-1)
startAt: 0, // initial seek position
ease: 'Sine.easeInOut' // any valid Phaser ease
});
// Control during playback
enemy.pauseFollow();
enemy.resumeFollow();
enemy.stopFollow();
enemy.isFollowing(); // returns boolean
// Change path at runtime
enemy.setPath(newPath);
enemy.setPath(newPath, { duration: 2000 }); // auto-starts
// Set rotation independently
enemy.setRotateToPath(true, 90); // (value, offsetDegrees)
使用简单持续时间的 PathFollower
// Shorthand: pass just a duration number
enemy.startFollow(5000);
// Equivalent to:
enemy.startFollow({ duration: 5000 });
所有曲线类型
| 曲线 | 类 | 构造函数参数 | 描述 |
|---|---|---|---|
| Line | Phaser.Curves.Line | (p0, p1) Vector2 端点,或 ([x0,y0,x1,y1]) | 两点之间的直线段 |
| Spline | Phaser.Curves.Spline | (points) Vector2 数组、扁平数字或嵌套数组 | 穿过控制点的 Catmull-Rom 样条曲线 |
| CubicBezier | Phaser.Curves.CubicBezier | (p0, p1, p2, p3) 或 ([x0,y0,...x3,y3]) | 具有起点、2 个控制点、终点的三次贝塞尔曲线 |
| QuadraticBezier | Phaser.Curves.QuadraticBezier | (p0, p1, p2) 或 ([x0,y0,...x2,y2]) | 具有起点、1 个控制点、终点的二次贝塞尔曲线 |
| Ellipse | Phaser.Curves.Ellipse | (x, y, xRadius, yRadius, startAngle, endAngle, clockwise, rotation) 或配置对象 | 椭圆弧;角度以度为单位;yRadius 默认为 xRadius |
椭圆曲线属性
椭圆曲线具有用于运行时修改的 get/set 属性:
x、y-- 中心位置xRadius、yRadius-- 半径startAngle、endAngle-- 以度为单位(get/set 在内部转换为弧度/从弧度转换)clockwise-- 布尔值rotation-- 以弧度为单位angle-- 以度为单位旋转(rotation的替代)setWidth(value)/setHeight(value)-- 将半径设置为 value/2
API 快速参考
Path (Phaser.Curves.Path)
| API | 类型 | 描述 |
|---|---|---|
add(curve) | 方法 | 将任意 Curve 追加到路径 |
lineTo(x, y) | 方法 | 从当前终点添加 Line |
splineTo(points) | 方法 | 从当前终点添加 Spline |
cubicBezierTo(x, y, cp1X, cp1Y, cp2X, cp2Y) | 方法 | 从当前终点添加 CubicBezier |
quadraticBezierTo(x, y, cpX, cpY) | 方法 | 从当前终点添加 QuadraticBezier |
ellipseTo(xR, yR, start, end, cw, rot) | 方法 | 从当前终点添加 Ellipse 弧 |
circleTo(radius, clockwise, rotation) | 方法 | 等半径 ellipseTo 的快捷方式 |
moveTo(x, y) | 方法 | 移动终点而不绘制(创建间隙) |
closePath() | 方法 | 如果尚未闭合,从终点到起点添加 Line |
getPoint(t, out) | 方法 | 整个路径上归一化位置(0-1)处的点 |
getPoints(divisions, stepRate) | 方法 | 点数组,每条曲线的 divisions |
getSpacedPoints(divisions) | 方法 | 沿整个路径的等距点 |
getRandomPoint(out) | 方法 | 路径上任意位置的随机点 |
getStartPoint(out) | 方法 | 路径起点 |
getEndPoint(out) | 方法 | 路径终点 |
getTangent(t, out) | 方法 | 位置 t 处的单位切向量 |
getCurveAt(t) | 方法 | 返回归一化位置 t 处的 Curve |
getLength() | 方法 | 路径总长度(像素) |
getCurveLengths() | 方法 | 累积曲线长度数组 |
getBounds(out, accuracy) | 方法 | 边界矩形 |
draw(graphics, pointsTotal) | 方法 | 将所有曲线绘制到 Graphics 对象上 |
toJSON() / fromJSON(data) | 方法 | 序列化 |
updateArcLengths() | 方法 | 强制重新计算缓存的长度 |
destroy() | 方法 | 清除内部引用 |
基础曲线(Phaser.Curves.Curve)
| API | 类型 | 描述 |
|---|---|---|
getPoint(t, out) | 方法 | 参数 t(0-1)处的点——抽象方法,由各子类实现 |
getPointAt(u, out) | 方法 | 弧长位置 u(0-1)处的点——均匀分布 |
getPoints(divisions, stepRate, out) | 方法 | 点数组 |
getSpacedPoints(divisions, stepRate, out) | 方法 | 按弧长等距分布的点 |
getDistancePoints(distance) | 方法 | 按像素距离间隔分布的点 |
getLength() | 方法 | 曲线总弧长 |
getTangent(t, out) / getTangentAt(u, out) | 方法 | 单位切向量 |
getTFromDistance(distance) | 方法 | 将像素距离转换为 t 值 |
draw(graphics, pointsTotal) | 方法 | 渲染到 Graphics 上(默认 32 个点) |
getBounds(out, accuracy) | 方法 | 边界矩形 |
active | boolean | 为 false 时,父 Path 会跳过此曲线 |
defaultDivisions | number | 独立曲线的默认值为 5 |
arcLengthDivisions | number | 弧长计算的精度(默认 100) |
PathFollower 组件
| API | 类型 | 描述 |
|---|---|---|
setPath(path, config) | 方法 | 设置新的 Path(可选自动开始) |
startFollow(config, startAt) | 方法 | 开始跟随;config = 持续时间数值或 PathConfig |
pauseFollow() | 方法 | 暂停移动 |
resumeFollow() | 方法 | 恢复暂停的移动 |
stopFollow() | 方法 | 停止跟随 |
isFollowing() | 方法 | 若正在路径上主动移动则返回 true |
setRotateToPath(value, offset) | 方法 | 启用/禁用带偏移的自动旋转 |
path | Path | 当前路径引用 |
pathTween | Tween | 驱动移动的内部补间 |
pathOffset | Vector2 | 相对于路径坐标的偏移 |
pathVector | Vector2 | 路径上的当前位置 |
pathDelta | Vector2 | 自上次更新以来的移动增量 |
rotateToPath | boolean | 自动旋转到路径方向 |
pathRotationOffset | number | 旋转偏移(以度为单位) |
PathConfig(Phaser.Types.GameObjects.PathFollower.PathConfig)
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
duration | number | 1000 | 遍历路径所需的时间(毫秒) |
from | number | 0 | 路径上的起始位置(0-1) |
to | number | 1 | 路径上的结束位置(0-1) |
positionOnPath | boolean | false | 开始时将跟随者对齐到路径起点 |
rotateToPath | boolean | false | 自动旋转以面向路径方向 |
rotationOffset | number | 0 | 添加到自动旋转上的度数 |
startAt | number | 0 | 路径上的初始寻道位置(0-1) |
该配置还接受所有标准 Tween 属性:ease、repeat、yoyo、delay、hold、onComplete 等。
注意事项
-
曲线上的
getPoint(t)与getPointAt(u)。getPoint使用原始曲线参数 t,在大多数曲线类型上不会产生均匀分布的点。getPointAt通过弧长映射来实现均匀间距。在 Path 上,getPoint已经考虑了整条路径上的弧长。 -
Path 的
moveTo会创建一条非活动曲线。MoveTo伪曲线的active: false且长度为零。它只为下一条曲线重新定位终点。它不绘制任何内容,并且会被getPoints()和draw()跳过。 -
PathFollower 内部使用 Tween。
startFollow配置会传给scene.tweens.addCounter()。所有 tween 属性(ease、delay、repeat、yoyo、callbacks)都有效。该 tween 会自动设置为persist: true。 -
PathFollower 的 offset 行为。 当
positionOnPath: false(默认值)时,follower 的当前位置会成为相对于路径起点的偏移量。当positionOnPath: true时,follower 会吸附到路径的起点,且偏移量被清零。 -
Ellipse 的角度以度为单位。 构造函数以及
startAngle/endAngle属性接受度数。内部以弧度存储。rotation属性以弧度为单位,但angle以度为单位。 -
closePath与autoClose。closePath()会添加一条从终点到起点的显式 Line 曲线。autoClose = true仅通过追加第一个点来影响getPoints()和getSpacedPoints()的输出,而不会添加曲线。 -
缓存长度可能会过期。
getCurveLengths()仅根据数组长度缓存结果。如果修改了曲线的控制点,请调用path.updateArcLengths()强制重新计算。 -
使用数字时
cubicBezierTo的参数顺序。 传入数字时:cubicBezierTo(endX, endY, cp1X, cp1Y, cp2X, cp2Y)。终点在前,而不是控制点。传入 Vector2 对象时:cubicBezierTo(cp1, cp2, endPoint)。 -
Spline 至少需要 4 个点。 Spline 使用的 Catmull-Rom 插值在 4 个点以上时效果最佳。点数更少时,曲线的行为可能不符合预期。
-
Line 曲线的
arcLengthDivisions为 1。 与其他曲线(默认为 100)不同,Line 将其覆盖为 1,因为直线本身就是均匀的。无需调整它。
源文件映射
| 文件 | 用途 |
|---|---|
src/curves/path/Path.js | Path 类——组合多条曲线,工厂注册为 this.add.path |
src/curves/path/MoveTo.js | 用于在路径中创建间隙的 MoveTo 伪曲线 |
src/curves/Curve.js | 基础 Curve 类——所有曲线类型共享的方法 |
src/curves/LineCurve.js | Line 曲线(两点线段) |
src/curves/SplineCurve.js | Spline 曲线(穿过多个点的 Catmull-Rom) |
src/curves/CubicBezierCurve.js | 三次贝塞尔曲线(4 个控制点) |
src/curves/QuadraticBezierCurve.js | 二次贝塞尔曲线(3 个控制点) |
src/curves/EllipseCurve.js | 支持角度和旋转的椭圆/弧线曲线 |
src/gameobjects/pathfollower/PathFollower.js | PathFollower 游戏对象(继承 Sprite + PathFollower mixin) |
src/gameobjects/pathfollower/PathFollowerFactory.js | this.add.follower 工厂注册 |
src/gameobjects/components/PathFollower.js | PathFollower 组件 mixin(setPath、startFollow、pathUpdate 等) |
src/gameobjects/pathfollower/typedefs/PathConfig.js | PathConfig 类型定义 |